Issue with SQL Group By and COALESCE on sqlite - sql

I have a table as below in sqlite database. I want to create a line chart showing usage by product groups.
Table: ProductUsageData
UserID ProductName ProductGroup Qty RecordID
1 A1 A 12 1
2 A1 A 12 1
1 A2 A 15 1
3 A1 A 12 2
2 B1 B 12 2
5 B2 B 5 2
1 A1 A 12 3
1 A2 A 15 3
4 A1 A 12 3
3 C1 C 12 3
2 C2 C 15 3
Since I want separate line for each ProductGroup I am using below Query
SELECT
SUM(Qty) as UsedQty,
ProductGroup,
RecordID
FROM ProductUsageData
GROUP BY ProductGroup, RecordID
ORDER BY RecordID ASC;
While I get three records for A (for each RecordID) I get only 1 record each for B & C as they are not used during each RecordID.
Problem is when I am putting one line for each ProductGroup in the chart, the points for B & C are shown as per Qty in the first
My output is like this
A 39 1
A 12 2
B 17 2
A 39 3
C 27 3
So the graph looks like this
instead of
To fix this I changed the query using COALESCE to get 0 Qty if the ProductGroup is not used during each recording.
SELECT
COALESCE(SUM(Qty), 0) as UsedQty,
ProductGroup,
RecordID
FROM ProductUsageData
GROUP BY ProductGroup, RecordID
ORDER BY RecordID ASC;
I was expecting output as below
A 39 1
B 0 1
C 0 1
A 12 2
B 17 2
C 0 2
A 39 3
B 0 3
C 27 3
But I am getting same output as first
Please let me know how can I correct the query to get desired output

A typical solution is to first cross join two queries that select the distinct product groups and record ids from the table; this gives you all possible combinations of productGroup and recordID.
Then, you can bring in the original table with a left join, and aggregate:
select
g.productGroup,
coalesce(sum(p.qty), 0) qty,
r.recordID
from (select distinct productGroup from productUsageData) g
cross join (select distinct recordID from productUsageData) r
left join productUsageData p
on p.productGroup = g.productGroup
and p.recordID = r.recordID
group by r.recordID, g.productGroup
order by r.recordID, g.productGroup
In the real world, you might have separate referential tables for product groups and records ids, which would make the query simpler and more efficient (since it would avoid the need to select distinct in subqueries).
Demo on DB Fiddle:
productGroup | qty | recordID
:----------- | :-- | :-------
A | 39 | 1
B | 0 | 1
C | 0 | 1
A | 12 | 2
B | 17 | 2
C | 0 | 2
A | 39 | 3
B | 0 | 3
C | 27 | 3

Related

How to count on join a table with 2 conditions?

I have an items table
id
name
1
Nganu
2
Kae
3
Lho
Also I have an item_usages table:
id
item_id
user_id
usage_time
1
1
99
2021-10-07 00:00:00
2
2
99
2021-10-07 00:00:00
3
1
99
2021-10-08 00:00:00
4
1
22
2021-10-08 00:00:00
5
3
22
2021-10-08 00:00:00
6
1
99
2021-10-08 00:00:00
I want to find an item's total usage and user usage in a query. an example I would like to find user_id 99 usage, expected result:
id
name
total_usage
user_usage
2
Kae
1
1
1
Nganu
4
3
3
Lho
1
0
I tried:
select
"items".*,
count(total_usage.id) as total_usage,
count(user_usage.id) as user_usage
from
"items"
left join
"item_usages" as "total_usage" on "items"."id" = "total_usage"."item_id"
left join
"item_usages" as "user_usage" on "user_usage"."item_id" = "items"."id"
and "user_usage"."user_id" = 99
group by
"items"."id";
but it returns:
id
name
total_usage
user_usage
2
Kae
1
1
1
Nganu
12
12
3
Lho
1
0
item_usages only have 6 rows, why Nganu have 12 on both usage? How to fix my query?
I tried on PostgreSQL 12.8 and 13.4, I also tested on SQLFiddle(PostgreSQL 9.6), Here is the link:
http://sqlfiddle.com/#!17/f1aac/5
I got the query that returned the correct result:
select
"items".*,
min(total_usage.total_count) as total_usage,
count(user_usage.id) as user_usage
from "items"
left join
(select item_id,count(item_id) as total_count from item_usages group by item_id) as total_usage
on "items"."id" = "total_usage"."item_id"
left join "item_usages" as "user_usage"
on "user_usage"."item_id" = "items"."id" and "user_usage"."user_id" = 99
group by "items"."id";
But I don't know about the performance, so I still find faster query if possible and still wondering:
Why does my first query give wrong result?
The reason your query returns high numbers is that you join 2 times.
(From the side of Nganu) The first join will result in 4 rows, the second will map those 4 rows with 3 rows of the same table, resulting in 12 rows.
You can solve this problem with only 1 join:
select "items".id,
count(total_usage.id) as total_usage,
sum(case when total_usage.user_id = 99 then 1 else 0 end) as user_usage
from "items"
left join "item_usages" as "total_usage" on "items"."id" = "total_usage"."item_id"
group by "items".id
And it should work faster (though, on a small dataset is not visible)

filling running total over all month although its null

I have 2 tables. One only with all periods. Second with Account, Amount and period.
I want to build a View that lists Amount kumulated, period and account. Also if I don't have an fact for a period in my table should be appear the period in my view with the last amount.
select distinct
account, b.periode,
SUM(amount) OVER (PARTITION BY account ORDER BY b.periode RANGE UNBOUNDED PRECEDING)
from
fakten a
full join
perioden b on a.periode = b.periode
order by b.periode
it like this:
1 1 6
2 1 4
1 2 13
2 2 3
NULL 3 NULL
1 4 46
2 5 48
NULL 6 NULL
NULL 7 NULL
1 8 147
NULL 9 NULL
NULL 10 NULL
NULL 11 NULL
NULL 12 NULL
I need it like:
1 1 6
2 1 4
1 2 13
2 2 3
1 3 13
2 3 3
1 4 46
2 4 3
1 5 46
2 5 48
1 6 46
2 6 46
and so one...
Any ideas?
full join is not the right approach. Instead, generate the rows you want using a cross join. Then use left join and group by to do the calculation.
select a.account, p.periode,
SUM(f.amount) OVER (PARTITION BY a.account ORDER BY p.periode)
from (select distinct account from fakten) a cross join -- you probably have an account table, use it
perioden p
on a.periode = p.periode left join
fakten f
on f.account = a.account and f.periode = p.periode
group by a.account, p.periode
order by a.account, p.periode;

Join two tables get count without using any where condition

I am trying to join two tables, prcshd (head table) and prcsdt (detail table), and need to get count of prod_prcshd_id in prcsdt (detail table) without using where (if required can use sub-query). Not sure ...
Tried like
select distinct count(b.prod_prcshd_id), b.prod_prcshd_id
from tra_pharmacy_prod_prcshd a join
tra_pharmacy_prod_prcsdt b
on b.prod_prcshd_id = a.id
group by b.dt_id
My tables:
prcshd (head table)
id(pk) | medi_name_id | med_prep_id
1 83 1
2 83 2
prcsdt (detail table)
dt_id(pk) | prod_prcshd_id(fk) | type_id | prod_name_id |medi_prep_id
1 1 4 83 1
2 1 5 83 1
3 1 6 83 1
4 2 4 83 2
still no luck.
id | prod_prcshd_id | medi_name_id
1 3 83
2 1 83
I'm not sure if this will fix your problem. But, you almost never need select distinct with group by. I suspect you want one of these this:
select b.prod_prcshd_id, count(b.prod_prcshd_id)
from tra_pharmacy_prod_prcshd a join
tra_pharmacy_prod_prcsdt b
on b.prod_prcshd_id = a.id
group by b.prod_prcshd_id;
Under most circumstances, you don't even need the join:
select ppp.prod_prcshd_id, count(*)
from tra_pharmacy_prod_prcsdt ppp
group by ppp.prod_prcshd_id;

Selecting rows and filler (null data)

I have a table that looks like this:
ReportID | TeamID | Inning | Runs
1 A 1 3
1 A 2 3
1 A 5 7
1 B 1 3
1 B 3 2
1 B 6 1
I need to select all of that data, plus null data for the missing innings. It also need to stop at the max Inning for both teams (i.e. teamB's highest inning is 6, so I would collect 6 rows for both teamA and teamB yielding 12 total rows.)
For a visual, I need the output of the query to look like this:
ReportID | TeamID | Inning | Runs
1 A 1 3
1 A 2 3
1 A 3 NULL
1 A 4 NULL
1 A 5 7
1 A 6 NULL
1 B 1 3
1 B 2 NULL
1 B 3 2
1 B 4 NULL
1 B 5 NULL
1 B 6 1
Is there anyway to do this with just a query? Modifying the original table to add the null values is not an option.
Self join to generate the permutations of reports and teams
Left self join to generate hits which might be nullable.
This is probably a lot more efficient if it's done outside of SQL
SELECT ins.ReportID, teams.TeamID, ins.inning, score.Runs
FROM games as ins
JOIN games AS teams
ON ins.ReportID = teams.ReportID
LEFT JOIN games AS score
ON ins.ReportID = score.ReportID
AND teams.TeamID = score.TeamID
AND ins.inning = score.inning
GROUP BY ins.ReportID, teams.TeamID, ins.inning;

How to apply Joins in Oracle to require result

I have Following 3 tables :
SHIFT_MASTER,PATTERN_MASTER,PATTERN_DETAILS
S_ID ,P_ID,P_D_ID are the priamry keys of SHIFT_MASTER,PATTERN_MASTER,PATTERN_DETAILS tables respectively.
SHIFT_MASTER
S_ID | S_NUMBER| S_Name
---------------------------------
1 A MORNING
2 B AFTERNOON
3 C NIGHT
PATTERN_MASTER
P_ID | P_NAME
----------------
1 Pattern 1
2 Pattern 2
PATTERN_DETAILS
P_D_ID|P_ID | S_ID| ...
---------------------
1 1 1
2 1 2
3 1 3
4 1 2
5 1 1
6 2 3
7 2 2
8 2 1
9 2 3
I GOT OUTPUT AS
P_ID | S_ID
1 1,2,3,2,1
2 3,2,1,3
USING QUERY
SELECT PATTERN_DETAILS.P_ID "PATTERN",
LISTAGG(PATTERN_DETAILS.S_ID, ', ')
WITHIN GROUP (ORDER BY PATTERN_DETAILS.P_D_ID) "SHIFT"
FROM PATTERN_DETAILS
GROUP BY PATTERN_DETAILS.P_ID;
WHAT I WANT IS
P_NAME | S_NUMBER
Pattern 1 A,B,C,B,A
Pattern 2 C,B,A,C
Any suggestion ??? Instead of P_ID i want to show pattern name and instead of shift id i want to show shift number .How to perform join operation along with listagg function ?
You need to join all three tables to get this,
SELECT pm.p_name "P_NAME",
listagg(sm.s_number, ', ') WITHIN GROUP (ORDER BY pd.p_d_id) "S_NUMBER"
FROM pattern_master pm,
pattern_details pd,
shift_master sm
WHERE sm.s_id= pd.s_id
AND pm.p_id = pd.p_id
GROUP BY pm.p_name;