postgres how to get desired result - postgresql

select count(*), templates.id as template_id,
templates.name,
checklists.status
from templates
left join checklists on checklists.template_id = templates.id and checklists.organization_id = 134 and checklists.company_id = 193
where templates.organization_id = 134
and (start_date between '2015-01-01' and '2020-01-01') and checklists.is_archived ='False'
group by templates.id, templates.name, checklists.status
it results
count | template_id | name | status
-------+-------------+-----------------+----------------
21 | 157 | asdsd | awaiting_reply
1 | 157 | asdsd | completed
36 | 157 | asdsd | pending
note here count is total number of checklist having status given in result
i.e for template_id 157, count is 21 having status awaiting reply
I want result should be reflected as
count | template_id | name | complete count | total count
-------+-------------+-----------------+----------------------+-------------------
21 | 157 | asdsd | 1 + 58
how to do it.

I can suggest the following updated version:
SELECT
t.template_id,
t.name,
COUNT(*) FILTER (WHERE c.status = 'completed') AS "complete count",
COUNT(*) AS "total count"
FROM templates t
LEFT JOIN checklists c
ON c.template_id = t.id AND
c.organization_id = 134 AND
c.company_id = 193
WHERE
t.organization_id = 134 AND
start_date BETWEEN '2015-01-01' AND '2020-01-01' AND
c.is_archived = 'False'
GROUP BY
t.template_id,
t.name;

If i understand well, based on your expected result, you want 3 counts :
1 for status = awaiting_reply (= 21 in your example 'count')
1 for status = completed (= 1 in your example 'complete count')
1 for total count (= 58 in your example 'total count')
I think you could achieve this with the following query :
select
templates.id as template_id,
templates.name,
SUM(checklists.status = 'awaiting_reply') AS 'count',
SUM(checklists.status = 'completed') AS 'complete_count',
COUNT(1) AS 'total_count'
from templates
left join checklists
on checklists.template_id = templates.id
and checklists.organization_id = 134
and checklists.company_id = 193
where templates.organization_id = 134
and (start_date between '2015-01-01' and '2020-01-01')
and checklists.is_archived ='False'
group by templates.id, templates.name

Related

Loop in the loop? PostgreSQL

I have one query.
Here:
do
$$
declare
_command text := 'update "T_Status_Furnace_MS" set "Temp_Min" = (select min(%2$I) from "Table_1_Furnace_1"
where "ID_TimeStamp_FK" > (select max("ID_TimeStamp_FK" ) -10 from "Table_1_Furnace_1")) where "P_ID" = %1$s';
begin for i in 1..100 loop execute format(_command , i , 't' || i);
end loop;
end
$$;
This query find on the mentioned table the last 10 row, find the minimum and the another table " Status" insert the minimum value.
But. If the last 10 row have 1000 or more that case check only the minimum after the 1000.
Example:
ID | values
100 | 950
99 | 950
98 | 900
97 | 920
96 | 1000
95 | 800
94 | 790
93 | 750
92 | 700
91 | 800
90 | 800
That case only check the minimum after the 1000 ( in the example only check the 96-97-98-99-100 ID because we have 1000 on the 96 ID )
This is possible? How? Use curzors? Or loop in the loop?
Thank you

Pivot sum in PostgreSQL

I'm using PosgreSQL 4.2. And I have the following code:
SELECT ID, Num1, Num2 FROM Tab1
WHERE ID IN(1,2,3,4.....50);
Returns results as:
ID | Num1 | Num2
----+--------------------
1 | 100 | 0
2 | 50 | 1
3 | 30 | 2
4 | 110 | 3
5 | 33 | 4
6 | 46 | 5
7 | 36 | 6
8 | 19 | 7
9 | 20 | 8
10 | 31 | 9
11 | 68 | 10
12 | 123 | 11
13 | 588 | 0
14 | 231 | 1
15 | 136 | 2
I want to Pivot sum to return result with pairs of number in IN clause, and result return like this:
| ID | Meaning
-------------------------
Num1 | 150 | 1+2(num1)
Num2 | 1 | 1+2(num2)
Num1 | 140 | 3+4(num1)
Num2 | 5 | 3+4(num2)
Num1 | 79 | 5+6(num1)
Num2 | 9 | 5+6(num2)
.........................
How can I do that?
PostgreSQL 4.2. No you must have another application ans confusing your application versions:
In 1996, the project was renamed to PostgreSQL to reflect its support
for SQL. The online presence at the website PostgreSQL.org began on
October 22, 1996.[26] The first PostgreSQL release formed version 6.0
on January 29, 1997.
Wikipedia. You might want to run the query: select version();
However, any supported version is sufficient for this request. Actually the SQL necessary to supply the necessary summations is simple once you understand LEAD (LAG) functions. The LEAD function permits access to the following row. Using that then the query:
select id, nid, n1s, n2s
from ( select id
, coalesce(lead(id) nid
, num1 + coalesce(lead(num1) over (order by id),0) n1s
, num2 + coalesce(lead(num2) over (order by id),0) n2s
, row_number() over() rn
from tab1
) i
where rn%2 = 1;
Provides all the necessary data for the presentation layer to format the the result as desired. However, that's not the result requested, that requires some SQL gymnastics.
We begin the gymnastics show by wrapping the above into an CTE adding a little setup for the show to follow. The main event then breaks the results into 2 sets in order to add the syntactical sugar tags before bringing then back together. So for the big show:
with joiner(id,nid,n1s,n2s,rn) as
( select *
from ( select id
, coalesce(lead(id) over (order by id),0)
, num1 + coalesce(lead(num1) over (order by id),0)
, num2 + coalesce(lead(num2) over (order by id),0)
, row_number() over() rn
from tab1
) i
where rn%2 = 1
)
select "Name","Sum","Meaning"
from (select 'Num1' as "Name"
, n1s as "Sum"
, concat(id::text, case when nid = 0
then null
else '+' || nid::text
end
) || ' (num1)'as "Meaning"
, rn
from joiner
union all
select 'Num2'
, n2s
, concat(id::text, case when nid = 0
then null
else '+' || nid::text
end
) || ' (num2)'
, rn
from joiner
) j
order by rn, "Name"
See fiddle. Note: I used "Sum" instead of ID for the column title, as it indicates the id not at all. That is just in the "Meaning" column.

Combining three very similar queries? (Postgres)

So I have three queries. I'm trying to combine them all into one query. Here they are with their outputs:
Query 1:
SELECT distinct on (name) name, count(distinct board_id)
FROM tablea
INNER JOIN table_b on tablea.id = table_b.id
GROUP BY name
ORDER BY name ASC
Output:
A | 15
B | 26
C | 24
D | 11
E | 31
F | 32
G | 16
Query 2:
SELECT distinct on (name) name, count(board_id) as total
FROM tablea
INNER JOIN table_b on tablea.id = table_b.id
GROUP BY 1, board_id
ORDER BY name, total DESC
Output:
A | 435
B | 246
C | 611
D | 121
E | 436
F | 723
G | 293
Finally, the last query:
SELECT distinct on (name) name, count(board_id) as total
FROM tablea
INNER JOIN table_b on tablea.id = table_b.id
GROUP BY 1
ORDER BY name, total DESC
Output:
A | 14667
B | 65123
C | 87426
D | 55198
E | 80612
F | 31485
G | 43392
Is it possible to format it to be like this:
A | 15 | 435 | 14667
B | 26 | 246 | 65123
C | 24 | 611 | 87426
D | 11 | 121 | 55198
E | 31 | 436 | 80612
F | 32 | 723 | 31485
G | 16 | 293 | 43392
EDIT:
With #Clodoaldo Neto 's help, I combined the first and the third queries with this:
SELECT name, count(distinct board_id), count(board_id) as total
FROM tablea
INNER JOIN table_b on tablea.id = table_b.id
GROUP BY 1
ORDER BY description ASC
The only thing preventing me from combining the second query with this new one is the GROUP BY clause needing board_id to be in it. Any thoughts from here?
This is hard to get right without test data. But here is my try:
with s as (
select name, grouping(name, board_id) as grp,
count(distinct board_id) as dist_total,
count(*) as name_total,
count(*) as name_board_total
from
tablea
inner join
table_b on tablea.id = table_b.id
group by grouping sets ((name), (name, board_id))
)
select name, dist_total, name_total, name_board_total
from
(
select name, dist_total, name_total
from s
where grp = 1
) r
inner join
(
select name, max(name_board_total) as name_board_total
from s
where grp = 0
group by name
) q using (name)
order by name
https://www.postgresql.org/docs/current/static/queries-table-expressions.html#QUERIES-GROUPING-SETS

Sql join and remove distinct in two separate column

I have table ordered
form_id | procedure_id
----------+-------------
101 | 24
101 | 23
101 | 22
102 | 7
102 | 6
102 | 3
102 | 2
And another table have table performed
form_id | procedure_id
----------+-------------
101 | 42
101 | 45
102 | 5
102 | 3
102 | 7
102 | 12
102 | 13
Expected output
form_id o_procedure_id p_procedure_id
101 24 42
101 23 45
101 22 NULL
102 7 7
102 6 5
102 3 3
102 2 12
102 NULL 13
I tried the below query:
with ranked as
(select
dense_rank() over (partition by po.form_id order by po.procedure_id) rn1,
dense_rank() over (partition by po.form_id order by pp.procedure_id) rn2,
po.form_id,
po.procedure_id,
pp.procedure_id
from ordered po,
performed pp where po.form_id = pp.form_id)
select ranked.* from ranked
--where rn1=1 or rn2=1
The above query return the value with repeat value ordered and procedure ID.
How to get Excepted output?
I wasn't quite sure how you would want to handle multiple null values and/or null values on both sides of your tables. My example therefor assumes the first table to be leading and include all entries while the second table might include holes. Query ain't pretty but i suppose it does what you expect it to:
select test1_sub.form_id, test1_sub.process_id as pid_1, test2_sub.process_id as pid_2 from (
select form_id,
process_id,
rank() over (partition by form_id order by process_id asc nulls last)
from test1) as test1_sub
left join (
select * from (
select form_id,
process_id,
rank() over (partition by form_id order by process_id asc nulls last)
from test2
) as test2_nonexposed
) as test2_sub on test1_sub.form_id = test2_sub.form_id and test1_sub.rank = test2_sub.rank;

Grouped LIMIT 10 in Postgresql

I have a query:
select
a.kli,
b.term_desc,
count(distinct(a.adic)) as count,
a.partner_id
from
ad_delivery.sgmt_kli_adic a
join wand.wandterms b on a.kli = b.term_code
join wand.wandterms c on b.term_desc=c.term_desc
join dwh.sgmt_clients e on a.partner_id::varchar = e.partner_id
join dwh.schema_names f on e.partner_id::integer = f.partner_id::integer
where
a.partner_id::integer in (f.partner_id)
and c.class_code = 969
group by a.partner_id, b.term_desc, a.kli
order by partner_id, count desc;
which brings back counts for certain terms per partner_id. I want to be able to show the top 10 for each of the ~40 partner_id in order by the count desc
the query results look like
db=# SELECT * FROM xxx;
pid | term_desc | count
----+------------+------
4 | termdesc1 | 3434
4 | termdesc2 | 235
4 | termdesc3 | 367
4 | termdesc4 | 4533
5 | termdesc1 | 235
5 | termdesc2 | 567
5 | termdesc3 | 344
5 | termdesc4 | 56
(10k+ rows)
You could add a rank column and then filter the result by the rank :
select
a.kli,
b.term_desc,
count(distinct(a.adic)) as count,
a.partner_id,
RANK() OVER (PARTITION BY a.partner_id order by a.partner_id DESC) AS r
from
ad_delivery.sgmt_kli_adic a
join wand.wandterms b on a.kli = b.term_code
join wand.wandterms c on b.term_desc=c.term_desc
join dwh.sgmt_clients e on a.partner_id::varchar = e.partner_id
join dwh.schema_names f on e.partner_id::integer = f.partner_id::integer
where
a.partner_id::integer in (f.partner_id)
and c.class_code = 969
group by a.partner_id, b.term_desc, a.kli
HAVING r < 11
order by partner_id, count desc;
I have not tested the code, however the trick is ranking the each row of the GROUP BY and filter the resultset with the HAVING clause, keeping only item with a lower rank than 11 (you will get 10 item per group).