Is it possible to aggregate rows like this in MySQL? - mysql

With the following table of car models,
+-------+----+-------+
| model | id | total |
+-------+----+-------+
| civic | 1 | 27 |
| civic | 3 | 11 |
| crv | 3 | 5 |
+-------+----+-------+
would it be possible to query in order to have this result?
+-------+-----+-----+-----+
| model | id1 | id2 | id3 |
+-------+-----+-----+-----+
| civic | 27 | 0 | 11 |
| crv | 0 | 0 | 5 |
+-------+-----+-----+-----+

This is a pivot query. One way to handle this in MySQL is conditional aggregation:
select model,
max(case when id = 1 then total else 0 end) as id1,
max(case when id = 2 then total else 0 end) as id2,
max(case when id = 3 then total else 0 end) as id3
from carmodels t
group by model;

Related

SQL query to return specific labels if exist (0 if it does not exist)

There are two tables given, tag and media.
mysql> select * from media;
+----+---------+----------+
| id | name | duration |
+----+---------+----------+
| 1 | cat.mp4 | 3.4 |
| 2 | dog.mp4 | 8 |
+----+---------+----------+
mysql> select * from tag;
+----+----------+-------+--------+------------+
| id | media_id | type | value | confidence |
+----+----------+-------+--------+------------+
| 1 | 1 | LABEL | cat | 0.9 |
| 2 | 1 | LABEL | person | 0.6 |
| 3 | 1 | TEXT | kitty | 0.95 |
| 4 | 2 | LABEL | dog | 0.8 |
| 5 | 2 | LABEL | person | 0.75 |
| 6 | 2 | TEXT | food | 0.7 |
+----+----------+-------+--------+------------+
I need to get the output table by joining two tables that gives media_id, name, duration and label of the value from tag such that if the value is cat, the confidence of cat will be inserted into label_cat column otherwise 0 will be inserted.
Something like this:
+----------+---------+----------+-----------+-----------+--------------+
| media_id | name | duration | label_cat | label_dog | label_person |
+----------+---------+----------+-----------+-----------+--------------+
| 1 | cat.mp4 | 3.4 | 0.9 | 0 | 0.6 |
| 2 | dog.mp4 | 8 | 0 | 0.8 | 0.75 |
+----------+---------+----------+-----------+-----------+--------------+
If I understand correctly, you want conditional aggregation:
select m.id, m.name, m.duration,
max(case when t.value = 'cat' then t.confidence end) as label_cat,
max(case when t.value = 'dog' then t.confidence end) as label_dog,
max(case when t.value = 'person' then t.confidence end) as label_person
from media m left join
tag t
on m.id = t.media_it
group by m.id, m.name, m.duration
Select t.media_id,m.name,m.duration,
case "label_cat " when t.value ='cat' then
t.confidence else 0 end case,
case "label_dog" when t.value ='dog' then
t.confidence else 0 end case,
case "label_person" when t.value ='person' then
t.confidence else 0 end case
from
tag t right join media m on t.id=m.id
group by t.media_id

How to sub total column value's in pivot table (MySQL) with group by clause

I have a pivot table with the respective fields.
district
block
MaleCount
FemaleCount
above are a column name. So, basically, I have grouped all those fileds by district and block-wise. For that, I have written the following query in MySQL.
select
ds.ds_name as district,
bs.bl_name as block,
case
when sd.gender_id = 1 then count(gender_id)
else 0
end as MaleCount,
case
when sd.gender_id = then count(gender_id)
else 0
end as FemalCount,
from studet data sd
inner join district ds on ds.district_id = sd.ds_id
inner join block bs on bs.block_id = sd.bs_id
group by bs.block_name, ds.district_name;
which gives the following result
district | block | FemalCount | MaleCount
xyz | abc | 4 | 5
| cvz | 5 | 9
ytz | tyz | 7 | 3
| awe |3 |8
The main thing is, I want the following kind of the result set.
district | block | FemalCount | MaleCount
xyz | abc | 4 | 5
| cvz | 5 | 9
total | | 9 | 14
ytz | tyz | 7 | 3
| awe | 3 | 8
total | | 10 | 11
which includes a subtotal row for Malecount and FemaleCount with a group by block and district as mention above. How could I achieve this in MySQL query?
You could add a union all (with high values for the block name to push it to the end on the order by) to calculate the totals by district so given
+----------+-----------+-------+-------+
| sudentid | gender_id | ds_id | bs_id |
+----------+-----------+-------+-------+
| 101 | 2 | 3 | 2 |
| 103 | 2 | 3 | 2 |
| 112 | 1 | 3 | 3 |
| 116 | 1 | 3 | 3 |
| 117 | 1 | 3 | 3 |
| 1 | 1 | 1 | 1 |
| 2 | 1 | 1 | 1 |
| 3 | 1 | 1 | 1 |
+----------+-----------+-------+-------+
8 rows in set (0.00 sec)
DROP TABLE IF EXISTS DISTRICT,BLOCK;
CREATE TABLE DISTRICT (ID INT, name varchar(10));
create table block(id int, name varchar(10));
insert into district values (1,'aaa'),(2,'bbb'),(3,'ccc'),(4,'ddd');
insert into block values (1,'E'),(2,'F'),(3,'G');
select
case when block = 'zzz' then 'Total'
else district
end as district,
case when block = 'zzz' then ''
else block
end as block,
malecount,femalecount
from
(
select
ds.name as district,
bs.name as block,
sum(case when gender_id = 1 then 1 else 0 end) Malecount,
sum(case when gender_id = 2 then 1 else 0 end) Femalecount
from student sd
inner join district ds on ds.id = sd.ds_id
inner join block bs on bs.id = sd.bs_id
group by ds.name, bs.name
union all
select district,'zzz', malecount,femalecount
from
(
select ds.name as district,
sum(case when gender_id = 1 then 1 else 0 end) Malecount,
sum(case when gender_id = 2 then 1 else 0 end) Femalecount
from student sd
inner join district ds on ds.id = sd.ds_id
group by ds.name
) s
) t
order by t.district,t.block
;
Result
+----------+-------+-----------+-------------+
| district | block | malecount | femalecount |
+----------+-------+-----------+-------------+
| aaa | E | 3 | 0 |
| Total | | 3 | 0 |
| ccc | F | 0 | 2 |
| ccc | G | 3 | 0 |
| Total | | 3 | 2 |
+----------+-------+-----------+-------------+
5 rows in set (0.00 sec)
Note the slightly less verbose conditional aggregation (sum(case when...)

Create 2 columns based on value in existing column

I have the following table. I would like to add 2 new columns with a select query that will show the total based on the flag type.
Table:
tt | company | count | flag
--------------------------------------------
123 | adeco | 5 | 1
123 | mic | 4 | 2
333 | manpower | 88 | 2
444 | linar | 2 | 2
555 | dlank | 3 | 1
Desired:
tt | company | total | flag | total_flag1 | total_flag2
-------------------------------------------------------------------
123 | adeco | 5 | 1 | 5 | 0
123 | mic | 4 | 2 | 0 | 4
333 | manpower | 88 | 2 | 0 | 88
444 | linar | 2 | 2 | 0 | 2
555 | dlank | 3 | 1 | 3 | 0
By your desired result, you should use case when or if syntax to to this:
select
yourtable.*,
case when flag = 1 then `count` else 0 end as total_flag1,
case when flag = 2 then `count` else 0 end as total_flag2
from yourtable
Or
select
yourtable.*,
if(flag = 1, `count`, 0) as total_flag1,
if(flag = 2, `count`, 0) as total_flag2
from yourtable
I think you can do what you want using correlated subqueries or join:
select t.*, tsum.total_flag1, tsum.total_flag2
from t join
(select t.tt,
sum(case when flag = 1 then total else 0 end) as total_flag1,
sum(case when flag = 2 then total else 0 end) as total_flag2
from t
group by t.tt
) tsum
on t.tt = tsum.tt;

efficient query to group by one field and put all other fields in the row SQL

Imagine the result of a query is something like the following:
+----+---------+--------+
| id | count | type |
+----+---------+--------+
| 1 | 20 | a |
| 1 | 30 | b |
| 1 | 10 | c |
| 2 | 05 | a |
| 2 | 20 | b |
| 2 | 40 | c |
+----+---------+--------+
and the expected result:
+----+---------+--------+------+
| id | a | b | c |
+----+---------+--------+------+
| 1 | 20 | 30 | 10 |
| 2 | 05 | 20 | 40 |
+----+---------+--------+------+
I know some solutions which are complex using Cursor, Variables, Join and etc. I would like to find the most efficient one, otherwise I will handle it from the application layer.
One method uses conditional aggregation:
select id,
sum(case when type = 'a' then count else 0 end) as a,
sum(case when type = 'b' then count else 0 end) as b,
sum(case when type = 'c' then count else 0 end) as c
from t
group by id;

Update innerquery result

I have a query and a result as follows.
In the database NULL and 0 represent the same meaning.
Now I want a counter based on Null+0 or 1
Eg:in the following example I want the result like this:
IsVirtual Category counter
NULL+0 3 343+8 = (351 is Total)
Query
select * from
(
Select IsVirtual, Category, count(*) as counter
from [Hardware]
group by IsVirtual, Category
) innercat
Output
+-----------+----------+---------+
| IsVirtual | Category | counter |
+-----------+----------+---------+
| NULL | 3 | 343 |
| 0 | 3 | 8 |
| 1 | 2 | 1 |
| 0 | 1 | 1 |
| NULL | 6 | 119 |
| 0 | 4 | 1 |
| NULL | 1 | 70 |
| 0 | 5 | 9 |
| NULL | 4 | 54 |
| 0 | 2 | 2 |
| NULL | 5 | 41 |
| NULL | 2 | 112 |
| 1 | 1 | 5 |
+-----------+----------+---------+
I think you want this :
SELECT COALESCE(IsVirtual, 0) as [IsVirtual],
Category,
Count(*) as [Counter]
FROM yourtable
GROUP BY COALESCE(IsVirtual, 0),Category
This will give you expected result without using subquery.
try with this
select * from (
Select CASE ISNULL(IsVirtual,0)
WHEN 0 Then 'NULL + 0'
ELSE IsVirtual
END AS IsVirtual, Category, count(*) as counter from [Hardware] group by ISNULL(IsVirtual,0), Category
)innercat
You can also do the same thing by using MAX function
This might help you.
SELECT
max(IsVirtual) as IsVirtual,
Category,
Count(*) as Counter
FROM
yourtable
GROUP BY
Category