How to check each value in particular group and set flag status? - mysql

Assume I have a Table "Emp"
Dpt_ID | E_ID | E_Status
A1 | 00200 | H
A1 | 00200 | M
A1 | 00400 | H
A1 | 00400 | M
A2 | 00300 | L
A2 | 00300 | M
A2 | 00400 | L
A2 | 00400 | L
Data needs to grouped according to Dpt_ID, If any E_ID under that Dpt_ID has status =H, then Set Flag=High. Similarly, if any E_ID under that Dpt_ID has status =M, then Set Flag=Medium else Low.
Currently I have written following query, but with group by function it checks only 1st row :
SELECT Dpt_ID,E_ID,E_Status,(CASE
WHEN E_Status = "H" THEN 'High'
WHEN E_Status = "M" THEN 'Medium'
ELSE 'Low'
END) AS Flag
FROM Emp
group by Dpt_ID
How do I check each value in Dpt_ID group to see if it's H/M/L ?
E.g. Target should be displayed as
Dpt_ID | Flag
A1 | High
A2 | Medium

The problem here is that you can't use MAX() directly because you have arbitrary char values, one option is assigning a weight to your values so you can use MAX(), and then get the flag using the weight values:
SELECT Dpt_ID,
CASE MAX(CASE E_Status WHEN 'H' THEN 2 WHEN 'M' THEN 1 ELSE 0 END)
WHEN 2 THEN 'High' WHEN 1 THEN 'Medium' ELSE 'Low' END AS Flag
FROM Emp
group by Dpt_ID

You are close. You can do:
SELECT Dpt_ID, E_ID, E_Status,
(CASE WHEN MAX(E_Status = 'H') THEN 'High'
WHEN MAX(E_Status = 'M') THEN 'Medium'
ELSE 'Low'
END) AS Flag
FROM Emp
GROUP BY Dpt_ID;

Related

join mysql select queries as 1 then group by

Using information of this site I have been able to do the join but im having the issues doing the group by
is there a way to get the below statements to run as 1
Query1
SELECT count(location),date
from `filter`
where location != "red"
group by date
Query2
SELECT count(location),date
from `filter`
where location = "red"
group by date
I did try the below but it outputs the wrong data
Query3
SELECT
date,
(select count(location) from `filter` where location != "red") AS indoor,
(select count(location) from `filter` where location = "red") AS outdoor
from `filter` group by date;
SQL Fiddle for each query
http://sqlfiddle.com/#!9/17ebea/4 (query1)
http://sqlfiddle.com/#!9/17ebea/6 (query2)
http://sqlfiddle.com/#!9/90c945/1 (query3)
SELECT
date,
COUNT(CASE WHEN location <> 'red' THEN location ELSE NULL END) AS indoor,
COUNT(CASE WHEN location = 'red' THEN location ELSE NULL END) AS outdoor
FROM filter
GROUP BY date;
https://dev.mysql.com/doc/refman/8.0/en/control-flow-functions.html#operator_case
You can do conditional aggregation using CASE.. WHEN expressions.
SELECT
date,
COUNT(CASE WHEN location = 'red' THEN location END) AS outdoor,
COUNT(CASE WHEN location <> 'red' THEN location END) AS indoor
FROM `filter`
GROUP BY date;
View on DB Fiddle
Result
| date | outdoor | indoor |
| ---------- | ------- | ------ |
| 2018-11-14 | 1 | 4 |
| 2018-11-15 | 1 | 0 |
| 2018-11-16 | 0 | 3 |
| 2018-11-17 | 1 | 1 |
| 2018-11-18 | 0 | 1 |
| 2018-11-19 | 0 | 2 |
| 2018-11-20 | 0 | 1 |
You can also use the following other variants, like using COUNT(1) instead, or using SUM(..) function.
Alternative #1
SELECT
date,
COUNT(CASE WHEN location = 'red' THEN 1 END) AS outdoor,
COUNT(CASE WHEN location <> 'red' THEN 1 END) AS indoor
FROM `filter`
GROUP BY date;
Alternative #2
SELECT
date,
SUM(CASE WHEN location = 'red' THEN 1 ELSE 0 END) AS outdoor,
SUM(CASE WHEN location <> 'red' THEN 1 ELSE 0 END) AS indoor
FROM `filter`
GROUP BY date;
In MySQL, I would use the shortcut that allows you to sum() boolean variables:
select date, sum(location = 'red') as red,
sum(location <> 'red') as not_red
from filter
group by date ;
Notes:
Use single quotes for string and date constants -- not double quotes. Single quotes are the standard delimiter.
<> is the SQL inequality operator, although != is also supported by most databases.
This does not count NULL values.
To handle NULL values, you might want:
sum(not location <=> 'red') as not_red

How to get the result in single row when using sub queries in SQL Server 2008?

I am trying to get a table with stage_name and its count in respective loan product. Like in below example stage_name is RCO and there are three loan product, Auto loan, Consumer loan and Credit card. Though I have used the logic and getting the right output, but in the output, I am getting the separate row for each stage_name and loan product case. I want only one row with all the three result. Please look at my code below, actual output and desired output:
SELECT
'RCO',
CASE
WHEN sq2.loan_type = 'Consumer loan'
THEN SUM(ISNULL(sq2.user_count, 0))
END AS Consumer_Loan,
CASE
WHEN sq2.loan_type = 'Auto Loan'
THEN SUM(ISNULL(sq2.user_count, 0))
END AS Auto_Loan,
CASE
WHEN sq2.loan_type = 'Credit Card'
THEN SUM(ISNULL(sq2.user_count, 0))
END AS Credit_Card
FROM
(SELECT
'RC0' AS ws_name, 'Consumer loan' AS loan_type,
COUNT(DISTINCT a.bpm_referenceno) AS user_count,
a.takenby AS user_id
FROM
BM_RLOS_DecisionHistoryForm a
INNER JOIN
(SELECT
m.bpm_referenceno
FROM
BM_RLOS_EXTTABLE m
WHERE
m.loan_type = 'Consumer Loan') sq1 ON a.bpm_referenceno = sq1.bpm_referenceno
WHERE
a.winame='RCO'
GROUP BY
a.takenby
UNION
SELECT 'RC0','Auto loan',
count (DISTINCT a.bpm_referenceno), a.takenby
from
BM_RLOS_DecisionHistoryForm a
INNER JOIN
(SELECT
m.bpm_referenceno
FROM BM_RLOS_EXTTABLE m
WHERE m.loan_type='Auto Loan')sq1
ON a.bpm_referenceno = sq1.bpm_referenceno
WHERE a.winame='RCO'
GROUP BY a.takenby
UNION
SELECT 'RC0','Credit Card',
count (DISTINCT a.bpm_referenceno), a.takenby
from
BM_RLOS_DecisionHistoryForm a
INNER JOIN
(SELECT
m.bpm_referenceno
FROM BM_RLOS_EXTTABLE m
WHERE m.loan_type='Credit Card')sq1
ON a.bpm_referenceno = sq1.bpm_referenceno
WHERE a.winame='RCO'
GROUP BY a.takenby) sq2
GROUP BY sq2.ws_name,sq2.loan_type
Actual output:
|--------------|-------------|-------------|-------------|
| Stg_nm | Cons_ln | Auto_lan | Credit_card |
|--------------|-------------|-------------|-------------|
| RCO | NULL | NULL | 8 |
|--------------|-------------|-------------|-------------|
| RCO | NULL | 55 | NULL |
|--------------|-------------|-------------|-------------|
| RCO | 81 | NULL | NULL |
|--------------|-------------|-------------|-------------|
Required Output
|--------------|-------------|-------------|-------------|
| Stg_nm | Cons_ln | Auto_lan | Credit_card |
|--------------|-------------|-------------|-------------|
| RCO | 81 | 55 | 8 |
|--------------|-------------|-------------|-------------|
The top half should be like this - reverse the usage of SUM and CASE, and remove the last GROUP BY altogether
SELECT
'RCO',
SUM(CASE
WHEN sq2.loan_type = 'Consumer loan'
THEN sq2.user_count
ELSE 0 END
)
AS Consumer_Loan,
SUM(CASE
WHEN sq2.loan_type = 'Auto loan'
THEN sq2.user_count
ELSE 0 END
)
AS Auto_Loan,
SUM(CASE
WHEN sq2.loan_type = 'Credit Card'
THEN sq2.user_count
ELSE 0 END
)
AS Credit_Card
FROM
<your existing query, with the final GROUP BY removed>
But you need to remove the GROUP BY from the end altogether

how to create pivots to avoid ID duplicates?

I Have the following table(´information´)
ID | value
1 | A
1 | B
2 | A
3 | A
3 | B
3 | C
4 | A
How can I use a query to render the following table?
ID | A | B | C |
1 X x null
2 X null null
3 X X X
4 X null null
I have the following code:
select id, CASE WHEN value= 'A' THEN 'X'
ELSE NULL END AS A,
CASE WHEN value= 'B' THEN 'X'
ELSE NULL END AS B,
CASE WHEN value= 'C' THEN 'X'
ELSE NULL END AS C
FROM information group by id;
but I ot one row per Id as well.
You can use conditional aggregation:
SELECT id,
MAX(CASE WHEN value= 'A' THEN 'X' END) AS A,
MAX(CASE WHEN value= 'B' THEN 'X' END) AS B,
MAX(CASE WHEN value= 'C' THEN 'X' END) AS C
FROM information
GROUP BY id;
Demo here
You just need aggregation functions:
select id,
MAX(CASE WHEN value = 'A' THEN 'X' END) AS A,
MAX(CASE WHEN value = 'B' THEN 'X' END) AS B,
MAX(CASE WHEN value = 'C' THEN 'X' END) AS C
FROM information
group by id;

SQL Aggregation with SUM, GROUP BY and JOIN (many-to-many)

Here's an example Table layout:
TABLE_A: TABLE_B: TABLE_A_B:
id | a | b | c id | name a_id | b_id
--------------------- --------- -----------
1 | true | X | A 1 | A 1 | 1
2 | true | Z | null 2 | B 1 | 2
3 | false | X | null 3 | C 2 | 2
4 | true | Y | Q 4 | 1
5 | false | null | null 4 | 2
5 | 1
Possible Values:
TABLE_A.a: true, false
TABLE_A.b: X, Y, Z
TABLE_A.c: A, B, C, ... basically arbitrary
TABLE_B.name: A, B, C, ... basically arbitrary
What I want to achieve:
SELECT all rows from TABLE_A
SUM(where a = true),
SUM(where a = false),
SUM(where b = 'X'),
SUM(where b = 'Y'),
SUM(where b = 'Z'),
SUM(where b IS NULL),
and also get the SUMs for all distinct TABLE_A.c values.
and also get the SUMs for all those TABLE_A_B relations.
The result for the example Table above should look like:
aTrue | aFalse | bX | bY | bZ | bNull | cA | cQ | cNull | nameA | nameB | nameC
-------------------------------------------------------------------------------
3 | 2 | 2 | 1 | 1 | 1 | 1 | 1 | 3 | 3 | 3 | 0
What I've done so far:
SELECT
SUM(CASE WHEN a = true THEN 1 ELSE 0 END) AS aTrue,
SUM(CASE WHEN b = false THEN 1 ELSE 0 END) AS aFalse,
SUM(CASE WHEN b = 'X' THEN 1 ELSE 0 END) AS bX,
...
FROM TABLE_A
What's my problem?
Selecting column TABLE_A.a and TABLE_A.b is easy, because there's a fixed number of possible values.
But I can't figure out how to count the distinct values of TABLE_A.c. And basically the same problem for the JOINed TABLE_B, because the number of values within TABLE_B is unknown and can change over time.
Thanks for your help! :)
EDIT1: New (preferred) SQL result structure:
column | value | sum
----------------------------
TABLE_A.a | true | 3
TABLE_A.a | false | 2
TABLE_A.b | X | 2
TABLE_A.b | Y | 1
TABLE_A.b | Z | 1
TABLE_A.b | null | 1
TABLE_A.c | A | 1
TABLE_A.c | Q | 1
TABLE_A.c | null | 3
TABLE_B.name | A | 3
TABLE_B.name | B | 3
TABLE_B.name | C | 0
From your original request of rows as a simulated pivot. By doing a SUM( logical condition ) basically returns 1 if true, 0 if false. So, since the column "a" is true or false, simple sum of "a" or NOT "a" (for the false counts -- NOT FALSE = TRUE). Similarly, your "b" column, so b='X' = true counted as 1, else 0.
In other sql engines, you might see it as SUM( case/when ).
Now, since your table counts don't rely on each other, they can be separate SUM() into their own sub-alias query references (pqA and pqB for pre-queryA and pre-queryB respectively). Since no group by, they will each result in a single row. With no join will create a Cartesian, but since 1:1 ratio, will only return a single record of all columns you want.
SELECT
pqA.*, pqB.*
from
( SELECT
SUM( ta.a ) aTrue,
SUM( NOT ta.a ) aFalse,
SUM( ta.b = 'X' ) bX,
SUM( ta.b = 'Y' ) bY,
SUM( ta.b = 'Z' ) bZ,
SUM( ta.b is null ) bNULL,
SUM( ta.c = 'A' ) cA,
SUM( ta.c = 'Q' ) cQ,
SUM( ta.c is null ) cNULL,
COUNT( distinct ta.c ) DistC
from
table_a ta ) pqA,
( SELECT
SUM( b.Name = 'A' ) nameA,
SUM( b.Name = 'B' ) nameB,
SUM( b.Name = 'C' ) nameC
from
table_a_b t_ab
join table_b b
ON t_ab.b_id = b.id ) pqB
This option gives your second (preferred) output
SELECT
MAX( 'TABLE_A.a ' ) as Basis,
CASE when a then 'true' else 'false' end Value,
COUNT(*) finalCnt
from
TABLE_A
group by
a
UNION ALL
SELECT
MAX( 'TABLE_A.b ' ) as Basis,
b Value,
COUNT(*) finalCnt
from
TABLE_A
group by
b
UNION ALL
SELECT
MAX( 'TABLE_A.c ' ) as Basis,
c Value,
COUNT(*) finalCnt
from
TABLE_A
group by
c
UNION ALL
SELECT
MAX( 'TABLE_B.name ' ) as Basis,
b.Name Value,
COUNT(*) finalCnt
from
table_a_b t_ab
join table_b b
ON t_ab.b_id = b.id
group by
b.Name
I think You will need to build dynamic query as you don't know possible values for column C in table A. So you can write store procedure where you can get list of distinct value for Column C in one variable and by using "Do WHILE" you can construct your dynamic query.
Please let me know if you need more help in detail
Dynamic SQL

MySQL Query that will group records

I have this table [Table 1]
cid | arrived | date_arrived
The [arrived field can have a value of [T] or [F], the value is [F] the date arrived field is NULL
1 records may appear only up to maximum of 2 (1 record for arrived=T and another record for arrived=F) But there are also records that may appear only once
1 | T | 2012-02-01
2 | F | [Null]
1 | F | [Null]
3 | T | 2012-03-05
I need a query that will show something like this
cid | arrived | not_arrived
1 Yes Yes
2 No Yes
3 Yes No
This works:
SELECT
cid,
SUM(arrived = 'T') > 0 as arrived,
SUM(arrived = 'F') > 0 as not_arrived
FROM [Table 1]
GROUP BY cid;
You can try it here: http://sqlfiddle.com/#!2/2b5a7/1/0
try
select cid,
case when find_in_set('T', group_concat(arrived)) > 0 then 'yes' else 'no' end as arrived,
case when find_in_set('F', group_concat(arrived)) > 0 then 'yes' else 'no' end as not_arrived
from table1
group by cid