SQL query sum of total corresponding rows - sql

I have two tables as below. Caseid from first table is referenced in second table along with accidents. What I am trying to get total different accidents for a case type. Below two tables I documented sample data and expected result.
Table case:
caseId CaseType
1 AB
2 AB
3 AB
4 CD
5 CD
6 DE
Table CaseAccidents:
AccidentId caseID AccidentRating
1 1 High
2 1 High
3 1 Medium
4 1 LOW
5 2 High
6 2 Medium
7 2 LOW
8 5 High
9 5 High
10 5 Medium
11 5 LOW
Result should look like:
CaseType TotalHIghrating TotalMediumRating TotalLOWRating
AB 3 2 2
CD 2 1 1
DE 0 0 0

To get the sum of every rating, you can Use a SUM(CASE WHEN) clause, adding 1 by every record that match the rating.
In your question, you have pointed out that you want to see all distinct CaseType, you can get it by using a RIGHT JOIN, this will include all records of case table.
select case.CaseType,
sum(case when caseAccidents.AccidentRating = 'High' then 1 else 0 end) as TotalHighRating,
sum(case when caseAccidents.AccidentRating = 'Medium' then 1 else 0 end) as TotalMediumRating,
sum(case when caseAccidents.AccidentRating = 'LOW' then 1 else 0 end) as TotalLowRating
from caseAccidents
right join case on case.caseId = caseAccidents.caseID
group by case.CaseType;
+----------+-----------------+-------------------+----------------+
| CaseType | TotalHighRating | TotalMediumRating | TotalLowRating |
+----------+-----------------+-------------------+----------------+
| AB | 3 | 2 | 2 |
+----------+-----------------+-------------------+----------------+
| CD | 2 | 1 | 1 |
+----------+-----------------+-------------------+----------------+
| DE | 0 | 0 | 0 |
+----------+-----------------+-------------------+----------------+
Check it: http://rextester.com/MCGJA9193

Have you use case in a select clause before?
select C.CaseType,
sum(case when CA.AccidentRating = 'High' then 1 else 0 end)
from Case C join CaseAccidents CA on C.CaseId = CA.CaseId
group by C.CaseType

Please see this. Sample query of the table and also that result
create table #case(caseid int,casetype varchar(5))
insert into #case (caseid,casetype)
select 1,'AB' union all
select 2,'AB' union all
select 3,'AB' union all
select 4,'CD' union all
select 5,'CD' union all
select 6,'DE'
create table #CaseAccidents(AccidentId int, CaseId int,AccidentRating varchar(10))
insert into #CaseAccidents(AccidentId, CaseId, AccidentRating)
select 1,1,'High' union all
select 2,1,'High' union all
select 3,1,'Medium' union all
select 4,1,'Low' union all
select 5,2,'High' union all
select 6,2,'Medium' union all
select 7,2,'Low' union all
select 8,5,'High' union all
select 9,5,'High' union all
select 10,5,'Medium' union all
select 11,5,'Low'
My script
select c.casetype,
sum(case when ca.AccidentRating='High' then 1 else 0 end) as TotalHighRating,
sum(case when ca.AccidentRating='Medium' then 1 else 0 end) as TotalMediumRating,
sum(case when ca.AccidentRating='Low' then 1 else 0 end) as TotalLowRating
from #case c
Left join #CaseAccidents ca
on c.Caseid=ca.Caseid
group by c.casetype
Hope This could help!

Another approach using Pivot operator
SELECT casetype,
[High],
[Medium],
[Low]
FROM (SELECT c.casetype,
AccidentRating
FROM case c
LEFT JOIN CaseAccidents ca
ON ca.CaseId = c.caseid)a
PIVOT (Count(AccidentRating)
FOR AccidentRating IN ([High],
[Medium],
[Low]) ) p

Try This code once.
select casetype,
sum(case when ca.AccidentRating='High' then 1 else 0 end ) as TotalHIghrating,
sum(case when ca.AccidentRating='Medium' then 1 else 0 end ) as TotalMediumRating ,
sum(case when ca.AccidentRating='Low' then 1 else 0 end ) as TotalLOWRating
from #case c
left join #CaseAccidents ca on c.caseid=ca.CaseId
group by casetype

Related

How to select data with group by and subquery calculations?

I have two tables:
list_table:
id
name
1
a
2
b
3
c
vt_table:
id
list_id
amount
direction_id
1
1
20
1
2
1
12
2
3
1
15
1
4
2
23
1
5
1
20
1
6
1
20
2
7
1
18
1
I need this result:
amount (dir_id = 1 - dir_id = 2), list_id
amount
list_id
41
1
23
2
0
3
Amount is sum of all amount fields in table vt_table where direction_id = 1 minus sum of all amount fileds in table vt_table where direction_id = 2
And I need group this calculations by list_id, and if table have no rows with list_id 3, as example, amount must be 0.
I'm trying to do it with this query:
SELECT vt.list_id
, ((SELECT COALESCE(SUM(vt.amount), 0)
FROM table_name vt
WHERE vt.direction_id = 1)
-
(SELECT COALESCE(SUM(vt.amount), 0)
FROM table_name vt
WHERE direction_id = 2)) AS result
FROM table_name vt
GROUP BY vt.list_id
But I don't know how to group it correctly and make it so that if there were no entries for some list_id, then the amount was 0 for this list_id.
I use PostgreSQL 12.
Here the examples
You can try to use OUTER JOIN with condition aggregate function with COALESCE fucntion.
Query 1:
SELECT l.id,
SUM(COALESCE(CASE WHEN vt.direction_id = 1 THEN vt.amount END,0)) -
SUM(COALESCE(CASE WHEN vt.direction_id = 2 THEN vt.amount END,0)) AS result
FROM table_name vt
RIGHT JOIN list l ON vt.list_id = l.id
GROUP BY l.id
ORDER BY l.id
Results:
| id | result |
|----|--------|
| 1 | 41 |
| 2 | 23 |
| 3 | 0 |
Try something like this, as a start:
SELECT vt.list_id
, COALESCE(SUM(CASE WHEN direction_id = 1 THEN amount END), 0)
- COALESCE(SUM(CASE WHEN direction_id = 2 THEN amount END), 0) AS result
FROM table_name vt
GROUP BY vt.list_id
;
Result using your fiddle:
list_id
result
1
41
2
23
This just misses the cases where there are no vt rows for some list.
Use an outer join to address those cases.
SELECT SUM(CASE WHEN vt.direction_id = 1 THEN vt.amount ELSE 0 END) - SUM(CASE WHEN vt.direction_id = 2 THEN vt.amount ELSE 0 END) as amount,
lt.id as list_id
FROM list_table lt
LEFT OUTER JOIN vt_table vt
ON lt.id = vt.list_id
GROUP BY lt.id
ORDER BY lt.id

Creating a view from two tables, with case and sum on one table

I'm having some troubles trying to create a view from two tables, which includes a sum + case for the first table. I've tried multiple different joins/unions, and I can get just the XTS table to come over, or just the case count scenarios to work, but I cannot get both.
here are the tables. For Table 1, UWI is non-unique. For Table 2, UWI is Unique. new_view is what I'm hoping to achieve for my view.
TABLE 1
UWI ET
1 A
1 B
1 B
2 B
2 C
2 C
TABLE 2
UWI XTS
1 10
2 20
3 10
4 30
new_view
UWI XTS B_COUNT C_COUNT
1 10 4 3
2 20 3 4
3 10 4 5
4 30 3 2
Here's what I'm currently working with.
CREATE VIEW new_view AS
SELECT t1.UWI,
sum(case when t1.ET='B' then 1 else 0 end) as B_COUNT,
sum(case when t1.ET='C' then 1 else 0 end) as C_COUNT,
sum(case when t1.ET='D' then 1 else 0 end) as D_COUNT,
sum(case when t1.ET='E' then 1 else 0 end) as E_COUNT,
sum(case when t1.ET='F' then 1 else 0 end) as F_COUNT
FROM TABLE_1 t1
INNER JOIN (SELECT t2.UWI, t2.XTS AS TSC
from TABLE_2 t2)
on t1.UWI = t2.UWI
group by t1.UWI;
Your sample select does not match your sample data, so this is a guess but I think you just need to move the aggregation into an apply()
select t2.UWI, t2.XTS, s.*
from Table_2 t2
outer apply (
select
sum(case when t1.ET='B' then 1 else 0 end) as B_COUNT,
sum(case when t1.ET='C' then 1 else 0 end) as C_COUNT,
sum(case when t1.ET='D' then 1 else 0 end) as D_COUNT,
sum(case when t1.ET='E' then 1 else 0 end) as E_COUNT,
sum(case when t1.ET='F' then 1 else 0 end) as F_COUNT
from table_1 t1
where t1.UWI = t2.UWI
group by t1.UWI
)s

Distinct Conditional Counting to Avoid Overlap

Consider this table:
[Table1]
------------------------
| Person_ID | Yes | No |
|-----------|-----|----|
| 1 | 1 | 0 |
|-----------|-----|----|
| 1 | 1 | 0 |
|-----------|-----|----|
| 2 | 0 | 1 |
|-----------|-----|----|
| 2 | 0 | 1 |
|-----------|-----|----|
| 3 | 1 | 0 |
|-----------|-----|----|
| 3 | 1 | 0 |
|-----------|-----|----|
| 3 | 0 | 1 |
|-----------|-----|----|
| 3 | 1 | 0 |
------------------------
I need a distinct count on Person_ID to get the number of people that are marked Yes and No. However, if someone has a single instance of No, they should be counted as a No and not be included in the Yes count no matter how many Yes they have.
My first thought was to try something similar to:
select count(distinct (case when Yes = 1 then Person_ID else null end)) Yes_People
, count(distinct (case when No = 1 then Person_ID else null end)) No_People
from Table1
but this will result in 3 being counted in both the Yes and No counts.
My desired output would be:
--------------------------
| Yes_People | No_People |
|------------|-----------|
| 1 | 2 |
--------------------------
I'm hoping to avoid the performance hit from having to evaluate a subquery against each row but if it has to be the way to go I will accept that.
Aggregate first at the person level and then overall:
select sum(yes_only) as yes_only,
sum(1 - yes_only) as no
from (select person_id,
(case when max(yes) = min(yes) and max(yes) = 1
then 1
end) as yes_only
from t
group by person_id
) t
You can first group them by the person.
Then the CASE for the Yes people can have a not No condition.
SELECT
COUNT(CASE WHEN No = 0 AND Yes = 1 THEN Person_ID END) AS Yes_People,
COUNT(CASE WHEN No = 1 THEN Person_ID END) AS No_People
FROM
(
select Person_ID
, MAX(Yes) as Yes
, MAX(No) as No
FROM Table1
GROUP BY Person_ID
) q
You could use a window function to rank the rows for a single person_id to prioritize a 'No' over a 'Yes', but that will require a subquery
select count(case when yes=1 then 1 end) as yes_count,
count(case when no=1 then no_count) as no_count
from (
select person_id, yes, no, row_number() over (order by no desc, yes desc) as rn
from table1
)
where rn = 1
The inner subquery plus the where filter will get you a single row per person_id, giving priority to the 'no' records.
This of course assumes yes/no are mutually exclusive, and if that's true, you should probably change the model to a single field.
Think you need to precheck every person with a window function
with t as (select 1 p_id, 1 yes, 0 no from dual
union all select 1 p_id, 1 yes, 0 no from dual
union all select 2 p_id, 0 yes, 1 no from dual
union all select 2 p_id, 0 yes, 1 no from dual
union all select 3 p_id, 1 yes, 0 no from dual
union all select 3 p_id, 0 yes, 1 no from dual
union all select 3 p_id, 1 yes, 0 no from dual)
, chk as (
select max(no) over (partition by p_id) n
, max(yes) over (partition by p_id) y
, p_id
from t)
-- select * from chk;
select count(distinct decode(y-n,1,p_id,null )) yes_people
, count(distinct decode(n,1,p_id,null )) no_people
from chk
group by 1;
You can use Conditional aggregation as following:
SQL> with table1 as (select 1 PERSON_ID, 1 yes, 0 no from dual
2 union all select 1 PERSON_ID, 1 yes, 0 no from dual
3 union all select 2 PERSON_ID, 0 yes, 1 no from dual
4 union all select 2 PERSON_ID, 0 yes, 1 no from dual
5 union all select 3 PERSON_ID, 1 yes, 0 no from dual
6 union all select 3 PERSON_ID, 0 yes, 1 no from dual
7 union all select 3 PERSON_ID, 1 yes, 0 no from dual)
8 SELECT
9 SUM(CASE WHEN NOS = 0 AND YES > 0 THEN 1 END) YES_PEOPLE,
10 SUM(CASE WHEN NOS > 0 THEN 1 END) NO_PEOPLE
11 FROM
12 (
13 SELECT
14 SUM(NO) NOS,
15 PERSON_ID,
16 SUM(YES) YES
17 FROM TABLE1
18 GROUP BY PERSON_ID
19 );
YES_PEOPLE NO_PEOPLE
---------- ----------
1 2
SQL>
Cheers!!

SQL: Get multiple line entries linked to one item?

I have a table:
ID | ITEMID | STATUS | TYPE
1 | 123 | 5 | 1
2 | 123 | 4 | 2
3 | 123 | 5 | 3
4 | 125 | 3 | 1
5 | 125 | 5 | 3
Any item can have 0 to many entries in this table. I need a query that will tell me if an ITEM has all it's entries in either a state of 5 or 4. For example, in the above example, I would like to end up with the result:
ITEMID | REQUIREMENTS_MET
123 | TRUE --> true because all statuses are either 5 or 4
125 | FALSE --> false because it has a status of 3 and a status of 5.
If the 3 was a 4 or 5, then this would be true
What would be even better is something like this:
ITEMID | MET_REQUIREMENTS | NOT_MET_REQUIREMENTS
123 | 3 | 0
125 | 1 | 1
Any idea how to write a query for that?
Fast, short, simple:
SELECT itemid
,count(status = 4 OR status = 5 OR NULL) AS met_requirements
,count(status < 4 OR status > 5 OR NULL) AS not_met_requirements
FROM tbl
GROUP BY itemid
ORDER BY itemid;
Assuming all columns to be integer NOT NULL.
Builds on basic boolean logic:
TRUE OR NULL yields TRUE
FALSE OR NULL yields NULL
And NULL is not counted by count().
->SQLfiddle demo.
SELECT a.ID FROM (SELECT ID, MIN(STATUS) AS MINSTATUS, MAX(STATUS) AS MAXSTATUS FROM TABLE_NAME AS a GROUP BY ID)
WHERE a.MINSTATUS >= 4 AND a.MAXSTATUS <= 5
One way of doing this would be
SELECT t1.itemid, NOT EXISTS(SELECT 1
FROM mytable t2
WHERE itemid=t1.itemid
AND status NOT IN (4, 5)) AS requirements_met
FROM mytable t1
GROUP BY t1.itemid
UPDATE: for your updated requirement, you can have something like:
SELECT itemid,
sum(CASE WHEN status IN (4, 5) THEN 1 ELSE 0 END) as met_requirements,
sum(CASE WHEN status IN (4, 5) THEN 0 ELSE 1 END) as not_met_requirements
FROM mytable
GROUP BY itemid
simple one:
select
"ITEMID",
case
when min("STATUS") in (4, 5) and max("STATUS") in (4, 5) then 'True'
else 'False'
end as requirements_met
from table1
group by "ITEMID"
better one:
select
"ITEMID",
sum(case when "STATUS" in (4, 5) then 1 else 0 end) as MET_REQUIREMENTS,
sum(case when "STATUS" in (4, 5) then 0 else 1 end) as NOT_MET_REQUIREMENTS
from table1
group by "ITEMID";
sql fiddle demo
WITH dom AS (
SELECT DISTINCT item_id FROM items
)
, yes AS ( SELECT item_id, COUNT(*) AS good_count FROM items WHERE status IN (4,5) GROUP BY item_id
)
, no AS ( SELECT item_id, COUNT(*) AS bad_count FROM items WHERE status NOT IN (4,5) GROUP BY item_id
)
SELECT d.item_id
, COALESCE(y.good_count,0) AS good_count
, COALESCE(n.bad_count,0) AS bad_count
FROM dom d
LEFT JOIN yes y ON y.item_id = d.item_id
LEFT JOIN no n ON n.item_id = d.item_id
;
Can be done with an outer join, too:
WITH yes AS ( SELECT item_id, COUNT(*) AS good_count FROM items WHERE status IN (4,5) GROUP BY item_id)
, no AS ( SELECT item_id, COUNT(*) AS bad_count FROM items WHERE status NOT IN (4,5) GROUP BY item_id)
SELECT COALESCE(y.item_id, n.item_id) AS item_id
, COALESCE(y.good_count,0) AS good_count
, COALESCE(n.bad_count,0) AS bad_count
FROM yes y
FULL JOIN no n ON n.item_id = y.item_id
;
Nevermind, it was actually easy to do:
select ITEM_ID ,
sum (case when STATUS >= 3 then 1 else 0 end ) as met_requirements,
sum (case when STATUS < 3 then 1 else 0 end ) as not_met_requirements
from TABLE as d
group by ITEM_ID

How to group sums by weekday in MySQL?

I have a table like this:
id | timestamp | type
-----------------------
1 2010-11-20 A
2 2010-11-20 A
3 2010-11-20 B
4 2010-11-21 A
5 2010-11-21 C
6 2010-11-27 B
and I need to count the rows for each type, grouped by weekday; like this:
weekday | A | B | C
--------------------------
5 2 2 0 -- the B column equals 2 because nov 20 and nov 27 are saturday
6 1 0 1
What would be the simplest solution for this?
I don't mind using views, variables, subqueries, etc.
Use:
SELECT WEEKDAY(t.timestamp) AS weekday,
SUM(CASE WHEN t.type = 'A' THEN 1 ELSE 0 END) AS a,
SUM(CASE WHEN t.type = 'B' THEN 1 ELSE 0 END) AS b,
SUM(CASE WHEN t.type = 'C' THEN 1 ELSE 0 END) AS c
FROM YOUR_TABLE t
GROUP BY WEEKDAY(t.timestamp)