MySQL - GROUP BY clause recursively? - mysql

Under MySQL I can not group the three separate data columns via GROUP BY clause.
This sample contains four brands, each brand has 2 models and 4 versions of engines. I need to extract 4 vehicles the brand, the model and version of engines is different. A truly unique result in the 3 columns.
----------+--------+----------+
Brand | Model | Version |
----------+--------+----------+
Renault | Clio | Essence |
Citroen | C4 | GPL |
Ford | Fiesta | Gazole |
Peugeot | 206 | Electric |
----------+--------+----------+
This works 8/10.
SELECT brand, model, version
FROM cars WHERE version = (SELECT version FROM cars GROUP BY version ORDER BY RAND() LIMIT 1)
GROUP BY brand
An example of the table
CREATE TABLE IF NOT EXISTS `cars` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`brand` varchar(255) NOT NULL,
`model` varchar(255) NOT NULL,
`version` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci ROW_FORMAT=COMPRESSED;
INSERT INTO `cars` (`id`, `brand`, `model`, `version`) VALUES
(1, 'Renault', 'Clio ', 'Essence'),
(2, 'Renault', 'Clio ', 'Gazole'),
(3, 'Renault', 'Clio ', 'GPL'),
(4, 'Renault', 'Clio ', 'Electric'),
(5, 'Renault', 'Modus', 'Essence'),
(6, 'Renault', 'Modus', 'Gazole'),
(7, 'Renault', 'Modus', 'GPL'),
(8, 'Renault', 'Modus', 'Electric'),
(9, 'Peugeot', '307', 'Essence'),
(10, 'Peugeot', '307', 'Gazole'),
(11, 'Peugeot', '307', 'GPL'),
(12, 'Peugeot', '307', 'Electric'),
(13, 'Peugeot', '206', 'Essence'),
(14, 'Peugeot', '206', 'Gazole'),
(15, 'Peugeot', '206', 'GPL'),
(16, 'Peugeot', '206', 'Electric'),
(17, 'Citroen', 'C4', 'Essence'),
(18, 'Citroen', 'C4', 'Gazole'),
(19, 'Citroen', 'C4', 'GPL'),
(20, 'Citroen', 'C4', 'Electric'),
(21, 'Citroen', 'C5', 'Essence'),
(22, 'Citroen', 'C5', 'Gazole'),
(23, 'Citroen', 'C5', 'GPL'),
(24, 'Citroen', 'C5', 'Electric'),
(25, 'Ford', 'Focus', 'Essence'),
(26, 'Ford', 'Focus', 'Gazole'),
(27, 'Ford', 'Focus', 'GPL'),
(28, 'Ford', 'Focus', 'Electric'),
(29, 'Ford', 'Fiesta', 'Essence'),
(30, 'Ford', 'Fiesta', 'Gazole'),
(31, 'Ford', 'Fiesta', 'GPL'),
(32, 'Ford', 'Fiesta', 'Electric');

If you need unique/distinct set of (Brand,Model,Version) add all three column name in GROUP BY clause like this:
SELECT brand, model, version FROM cars
WHERE version = (SELECT version FROM cars GROUP BY version ORDER BY RAND() LIMIT 1)
GROUP BY brand, model, version

Here is what I was offered, it is not optimized but it works.
If someone with a suggestion I'm interested.
CREATE TABLE IF NOT EXISTS temp AS
SELECT
tb1.id AS id1,
tb1.brand AS brand1,
tb1.model AS model1,
tb1.version AS version1,
tb2.id AS id2,
tb2.brand AS brand2,
tb2.model AS model2,
tb2.version AS version2,
tb3.id AS id3,
tb3.brand AS brand3,
tb3.model AS model3,
tb3.version AS version3,
tb4.id AS id4,
tb4.brand AS brand4,
tb4.model AS model4,
tb4.version AS version4
FROM `cars` AS tb1
INNER JOIN `cars` AS tb2
ON tb2.brand != tb1.brand
AND tb2.model != tb1.model
AND tb2.version != tb1.version
AND tb2.id > tb1.id
INNER JOIN `cars` AS tb3
ON tb3.brand != tb2.brand
AND tb3.model != tb2.model
AND tb3.version != tb2.version
AND tb3.id > tb2.id
AND tb3.brand != tb1.brand
AND tb3.model != tb1.model
AND tb3.version != tb1.version
AND tb3.id > tb1.id
INNER JOIN `cars` AS tb4
ON tb4.brand != tb3.brand
AND tb4.model != tb3.model
AND tb4.version != tb3.version
AND tb4.id > tb3.id
AND tb4.brand != tb2.brand
AND tb4.model != tb2.model
AND tb4.version != tb2.version
AND tb4.id > tb2.id
AND tb4.brand != tb1.brand
AND tb4.model != tb1.model
AND tb4.version != tb1.version
AND tb4.id > tb1.id
LIMIT 1
--------------
select * from sample
--------------
+-----+---------+--------+----------+-----+---------+--------+----------+-----+---------+--------+----------+-----+--------+--------+----------+
| id1 | brand1 | model1 | version1 | id2 | brand2 | model2 | version2 | id3 | brand3 | model3 | version3 | id4 | brand4 | model4 | version4 |
+-----+---------+--------+----------+-----+---------+--------+----------+-----+---------+--------+----------+-----+--------+--------+----------+
| 1 | Renault | Clio | Essence | 10 | Peugeot | 307 | Gazole | 19 | Citroen | C4 | GPL | 28 | Ford | Focus | Electric |
+-----+---------+--------+----------+-----+---------+--------+----------+-----+---------+--------+----------+-----+--------+--------+----------+
and get the result formatter
SELECT id1 AS id,
brand1 AS brand,
model1 AS model,
version1 AS version
FROM temp
UNION
SELECT id2 AS id,
brand2 AS brand,
model2 AS model,
version2 AS version
FROM temp
UNION
SELECT id3 AS id,
brand3 AS brand,
model3 AS model,
version3 AS version
FROM temp
UNION
SELECT id4 AS id,
brand4 AS brand,
model4 AS model,
version4 AS version
FROM temp
+----+---------+-------+----------+
| id | brand | model | version |
+----+---------+-------+----------+
| 1 | Renault | Clio | Essence |
| 10 | Peugeot | 307 | Gazole |
| 19 | Citroen | C4 | GPL |
| 28 | Ford | Focus | Electric |
+----+---------+-------+----------+

Related

MySQL query to show data separated by comma with group by different columns

I have a MySQL table where I have stored all of users searches. So the table looks something like this
CREATE TABLE `users_search_activity` (
`ID` bigint(20) UNSIGNED NOT NULL,
`user_id` int(11) NOT NULL,
`country_id` int(11) NOT NULL,
`search_keywords` text COLLATE utf8mb4_unicode_ci NOT NULL,
`date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
-- Dumping data for table `users_search_activity`
--
INSERT INTO `users_search_activity` (`ID`, `user_id`, `country_id`, `search_keywords`, `date`) VALUES
(1, 132, 2, 'xavie', '2021-07-13 08:20:37'),
(2, 132, 6, 'xavier', '2021-07-13 08:21:38'),
(3, 132, 5, 'xavier ins', '2021-07-13 08:21:39'),
(4, 132, 4, 'xavier ins', '2021-07-13 08:21:39'),
(5, 131, 9, 'xavier ins', '2021-07-13 08:22:12'),
(6, 132, 7, 'xavier ins', '2021-07-13 08:22:25'),
(7, 132, 8, 'xavier ins', '2021-07-13 09:24:43'),
(8, 132, 6, 'xavier ins', '2021-07-13 09:24:45'),
(9, 132, 4, 'xavier insa', '2021-07-13 09:24:47'),
(10, 131, 5, 'ins', '2021-07-13 09:24:54'),
(11, 132, 3, 'ins', '2021-07-13 09:24:54'),
(12, 132, 2, 'ins', '2021-07-13 09:24:58'),
(13, 132, 9, 'ins', '2021-07-13 09:24:59'),
(14, 132, 0, 'ins', '2021-07-13 09:25:00'),
(15, 132, 0, 'ins', '2021-07-13 09:25:02'),
(16, 132, 0, 'inst', '2021-07-13 09:58:20'),
(17, 132, 0, 'inst', '2021-07-04 09:58:25'),
(18, 132, 0, 'inst', '2021-07-07 09:58:25'),
(19, 132, 0, 'inst', '2021-07-11 09:58:26'),
(20, 1, 12, 'University Business Academy in Novi Sad', '2021-07-14 10:16:33');
--
-- Indexes for dumped tables
--
--
-- Indexes for table `users_search_activity`
--
ALTER TABLE `users_search_activity`
ADD PRIMARY KEY (`ID`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `users_search_activity`
--
ALTER TABLE `users_search_activity`
MODIFY `ID` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=20;
COMMIT;
Now I want to make some query from where I can get the data group by country_id and date.
So for that I have made my query like this
WITH cte AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY country_id, DATE(date) ORDER BY ID) rn, COUNT(*) OVER (PARTITION BY country_id, DATE(date)) cnt FROM users_search_activity ) SELECT ID, cnt AS count, search_keywords, user_id, country_id, DATE(date) as date FROM cte WHERE rn = 1;
Here its working fine except the search_keywords. It is only showing single search_keywords. I want to show them all one by one separated by comma for the given date and country_id.
So can someone tell me how to do that? Any help or suggestions would be really appreciable.
Thanks,
The output should be something like this
count user_id country_id date search_keywords
1 132 4 2021-07-13 xavier ins, xavier insa
you can use group_concat
WITH cte AS (
SELECT *,DATE(date),ROW_NUMBER() OVER (PARTITION BY country_id, DATE(date) ORDER BY ID) rn,
COUNT(*) OVER (PARTITION BY country_id, DATE(date)) cnt
FROM users_search_activity
)
,tab2 as (
select t1.country_id,
date(date) dat,
group_concat(t1.search_keywords)
from cte t1
group by t1.country_id,
date(date)
)
SELECT *
FROM cte t1,
tab2 t2
WHERE t1.rn = 1
and t1.country_id = t2.country_id
and DATE(t1.date) = t2.dat
;
update at 2021/8/3
5.7 also has group_concat,you just need to deal with row_number.
select t1.country_id,
date(date) dat,
group_concat(t1.search_keywords),
max(case when t1.row_number = 1 then t1.user_id else null end) user_id
from (
select t1.*,
#rn := case when #temp1 is null then 1
when #temp1 = t1.country_id and #temp2 = DATE(date) then 0
else 1
end row_number,
#temp1:= t1.country_id,
#temp2:= DATE(date)
from users_search_activity t1,(select #rn:=0, #temp1:='', #temp2:='') t2
order by country_id, DATE(date), t1.id
) t1
group by t1.country_id,
date(date)
;
First of all thanks for providing such detail information in text format. Your question deserves upvote for that.
You don't need any ranking function like row_number() or cte. Simple group by with group_concat() is enough.
Query:
SELECT COUNT(*) OVER (PARTITION BY country_id, DATE(date)) AS count,
user_id, country_id, DATE(date) as date, group_concat(search_keywords) FROM users_search_activity
group by user_id,country_id,DATE(date)
Output:
|count | user_id | country_id | date | group_concat(search_keywords)
|----: | ------: | ---------: | :--------- | :--------------------------------------
| 1 | 132 | 0 | 2021-07-04 | inst
| 1 | 132 | 0 | 2021-07-07 | inst
| 1 | 132 | 0 | 2021-07-11 | inst
| 1 | 132 | 0 | 2021-07-13 | inst,ins,ins
| 1 | 132 | 2 | 2021-07-13 | xavie,ins
| 1 | 132 | 3 | 2021-07-13 | ins
| 1 | 132 | 4 | 2021-07-13 | xavier insa,xavier ins
| 2 | 131 | 5 | 2021-07-13 | ins
| 2 | 132 | 5 | 2021-07-13 | xavier ins
| 1 | 132 | 6 | 2021-07-13 | xavier ins,xavier
| 1 | 132 | 7 | 2021-07-13 | xavier ins
| 1 | 132 | 8 | 2021-07-13 | xavier ins
| 2 | 131 | 9 | 2021-07-13 | xavier ins
| 2 | 132 | 9 | 2021-07-13 | ins
| 1 | 1 | 12 | 2021-07-14 | University Business Academy in Novi Sad
db<>fiddle here

What is the right way to use group by clause in MySQL with multiple conditions?

I am writing a query that will find the youngest student by each major if their average grade score is more than 80 and order them by their name from the following relation. I am using MySQL server and working with MySQL Workbench.
Student:
snum: integer
name: string
major: string
level: string
age: integer
Class:
cname: string
meets_at: time
room: string
fid: integer
Grade:
snum (foreign key)
name (foreign key)
score
Here is how I tried to implement the query.
select S.major, S.name, S.age
from student S , grades G
group by S.major
Having MIN(S.age) and G.score > (Select avg(G.score)
from grades G1 , student S
where S.snum = G1.snum) ;
However this doesn't work and I am really confused about what the query should look like.
Sample data:
CREATE TABLE students
(`snum` int, `name` varchar(18), `major` varchar(22), `standing` varchar(2),
`age` int)
;
INSERT INTO student
(`snum`, `name`, `major`, `standing`, `age`)
VALUES
(578875478, 'Edward Baker', 'Veterinary Medicine', 'SR', 21),
(574489456, 'Betty Adams', 'Economics', 'JR', 20),
(573284895, 'Steven Green', 'Kinesiology', 'SO', 19),
(567354612, 'Karen Scott', 'Computer Engineering', 'FR', 18),
(556784565, 'Kenneth Hill', 'Civil Engineering', 'SR', 21),
(552455318, 'Ana Lopez', 'Computer Engineering', 'SR', 19),
(550156548, 'George Wright', 'Education', 'SR', 21),
(462156489, 'Donald King', 'Mechanical Engineering', 'SO', 19),
(455798411, 'Luis Hernandez', 'Electrical Engineering', 'FR', 17),
(451519864, 'Mark Young', 'Finance', 'FR', 18),
(351565322, 'Nancy Allen', 'Accounting', 'JR', 19),
(348121549, 'Paul Hall', 'Computer Science', 'JR', 18),
(322654189, 'Lisa Walker', 'Computer Science', 'SO', 17),
(320874981, 'Daniel Lee', 'Electrical Engineering', 'FR', 17),
(318548912, 'Dorthy Lewis', 'Finance', 'FR', 18),
(301221823, 'Juan Rodriguez', 'Psychology', 'JR', 20),
(280158572, 'Margaret Clark', 'Animal Science', 'FR', 18),
(269734834, 'Thomas Robinson', 'Psychology', 'SO', 18),
(132977562, 'Angela Martinez', 'History', 'SR', 20),
(115987938, 'Christopher Garcia', 'Computer Science', 'JR', 20),
(112348546, 'Joseph Thompson', 'Computer Science', 'SO', 19),
(99354543, 'Susan Martin', 'Law', 'JR', 20),
(60839453, 'Charles Harris', 'Architecture', 'SR', 22),
(51135593, 'Maria White', 'English', 'SR', 21);
CREATE TABLE grades
(`snum` int, `cname` varchar(23), `score` int);
INSERT INTO grades
(`snum`, `cname`, `score`)
VALUES
(574489456, 'Urban Economics', 45),
(567354612, 'Operating System Design', 98),
(567354612, 'Data Structures', 100),
(552455318, 'Operating System Design', 98),
(552455318, 'Communication Networks', 87),
(455798411, 'Operating System Design', 100),
(455798411, 'Optical Electronics', 87),
(348121549, 'Database Systems', 90),
(322654189, 'Database Systems', 97),
(322654189, 'Operating System Design', 56),
(301221823, 'Perception', 87),
(301221823, 'Social Cognition', 87),
(115987938, 'Database Systems', 100),
(115987938, 'Operating System Design', 98),
(112348546, 'Database Systems', 80),
(112348546, 'Operating System Design', 35),
(99354543, 'Patent Law', 65)
;
Expected Results:
+------------------------+----------------+----+---------+---+
| Computer Engineering | Karen Scott | 18 | 99.0000 | 1 |
+------------------------+----------------+----+---------+---+
| Computer Science | Paul Hall | 18 | 90.0000 | 1 |
+------------------------+----------------+----+---------+---+
| Electrical Engineering | Luis Hernandez | 17 | 93.5000 | 1 |
+------------------------+----------------+----+---------+---+
| Psychology | Juan Rodriguez | 20 | 87.0000 | 1 |
+------------------------+----------------+----+---------+---+
Here is an approach that might work for your use case. The logic is to combine aggregation and window functions.
First, you can use a simple aggregate query to compute the average score of each student:
SELECT s.major, s.name, s.age, AVG(g.score) avg_score
FROM
students s
INNER JOIN grades g ON g.snum = s.snum
GROUP BY s.snum, s.major, s.name, s.age
HAVING AVG(g.score) > 80
This will give you one record per student whose average score is higher than 80, along with his age, name and major, and average score.
Now all that is left to do is to select the youngest student in each group of students that have the same major. This can be done with window function ROW_NUMBER() :
SELECT major, name, age, avg_score
FROM (
SELECT
x.*,
ROW_NUMBER() OVER(PARTITION BY major ORDER BY age) rn
FROM (
SELECT s.major, s.name, s.age, AVG(g.score) avg_score
FROM
students s
INNER JOIN grades g ON g.snum = s.snum
GROUP BY s.snum, s.major, s.name, s.age
HAVING AVG(g.score) > 80
) x
) z WHERE rn = 1
This DB Fiddle with your sample data returns:
| major | name | age | avg_score |
| ---------------------- | -------------- | --- | --------- |
| Computer Engineering | Karen Scott | 18 | 99 |
| Computer Science | Paul Hall | 18 | 90 |
| Electrical Engineering | Luis Hernandez | 17 | 93.5 |
| Psychology | Juan Rodriguez | 20 | 87 |

mysql running difference with group by

Dataset I am experimenting has the structure as given in this SQLFiddle.
create table readings_tab (id int, site varchar(15), logged_at datetime, reading smallint);
insert into readings_tab values (1, 'A', '2017-08-21 13:22:00', 2500);
insert into readings_tab values (2, 'B', '2017-08-21 13:22:00', 1210);
insert into readings_tab values (3, 'C', '2017-08-21 13:22:00', 3500);
insert into readings_tab values (4, 'A', '2017-08-22 13:22:00', 2630);
insert into readings_tab values (5, 'B', '2017-08-22 13:22:00', 1400);
insert into readings_tab values (6, 'C', '2017-08-22 13:22:00', 3800);
insert into readings_tab values (7, 'A', '2017-08-23 13:22:00', 2700);
insert into readings_tab values (8, 'B', '2017-08-23 13:22:00', 1630);
insert into readings_tab values (9, 'C', '2017-08-23 13:22:00', 3950);
insert into readings_tab values (10, 'A', '2017-08-24 13:22:00', 2850);
insert into readings_tab values (11, 'B', '2017-08-24 13:22:00', 1700);
insert into readings_tab values (12, 'C', '2017-08-24 13:22:00', 4200);
insert into readings_tab values (13, 'A', '2017-08-25 13:22:00', 3500);
insert into readings_tab values (14, 'B', '2017-08-25 13:22:00', 2300);
insert into readings_tab values (15, 'C', '2017-08-25 13:22:00', 4700);
Current Query:
select t.rownum, t.logged_on, t.tot_reading, coalesce(t.tot_reading - t3.tot_reading, 0) AS daily_generation
from
(
select #rn:=#rn+1 AS rownum, date(t.logged_at) AS logged_on, sum(t.reading) AS tot_reading
from readings_tab t, (SELECT #rn:=0) t2
group by date(t.logged_at)
order by date(t.logged_at) desc
) t
left join
(
select #rn:=#rn+1 AS rownum, date(t.logged_at) AS logged_on, sum(t.reading) AS tot_reading
from readings_tab t, (SELECT #rn:=0) t2
group by date(t.logged_at)
order by date(t.logged_at) desc
) t3 on t.rownum = t3.rownum + 1
order by t.logged_on desc;
I am expecting below output. I don't need the formula (3500+2300+4700, etc...) in the result set. Just included it to make it understandable.
-----------------------------------------------------------------
| logged_on | tot_reading | daily_generation |
-----------------------------------------------------------------
| 2017-08-25 | (3500+2300+4700) = 10500 | (10500 - 8750) = 1750 |
| 2017-08-24 | (2850+1700+4200) = 8750 | (8750-8280) = 470 |
| 2017-08-23 | (2700+1630+3950) = 8280 | (8280-7830) = 450 |
| 2017-08-22 | (2630+1400+3800) = 7830 | (7830-7210) = 620 |
| 2017-08-21 | (2500+1210+3500) = 7210 | 0 |
-----------------------------------------------------------------
I cannot figure out why it doesn't produce expected output. Can someone please help?
If using variables make sure they are unique to each subquery else you can get incorrect results. I suggest the following adjusted query (which has some added columns to help follow what is happening):
select
t.rownum, t.logged_on, t.tot_reading
, coalesce(t.tot_reading - t3.tot_reading, 0) AS daily_generation
, t3.rownum t3_rownum
, t3.tot_reading t3_to_read
, t.tot_reading t_tot_read
from
(
select #rn:=#rn+1 AS rownum, date(t.logged_at) AS logged_on, sum(t.reading) AS tot_reading
from readings_tab t
cross join (SELECT #rn:=0) t2
group by date(t.logged_at)
order by date(t.logged_at) desc
) t
left join
(
select #rn2:=#rn2+1 AS rownum, date(t.logged_at) AS logged_on, sum(t.reading) AS tot_reading
from readings_tab t
cross join (SELECT #rn2:=0) t2
group by date(t.logged_at)
order by date(t.logged_at) desc
) t3 on t.rownum = t3.rownum + 1
order by t.logged_on desc
;
Note I also recommend using explicit CROSS JOIN syntax as it leads to easier comprehension for anyone who needs to maintain this query.
Here is the result (& also see http://sqlfiddle.com/#!9/dcb5e2/1 )
| rownum | logged_on | tot_reading | daily_generation | t3_rownum | t3_to_read | t_tot_read |
|--------|------------|-------------|------------------|-----------|------------|------------|
| 5 | 2017-08-25 | 10500 | 1750 | 4 | 8750 | 10500 |
| 4 | 2017-08-24 | 8750 | 470 | 3 | 8280 | 8750 |
| 3 | 2017-08-23 | 8280 | 450 | 2 | 7830 | 8280 |
| 2 | 2017-08-22 | 7830 | 620 | 1 | 7210 | 7830 |
| 1 | 2017-08-21 | 7210 | 0 | (null) | (null) | 7210 |

Extend given long MySQL query and get count of completed and not completed

When I write below MySQL query, it gives me correct output like mentioned after that I want to get completed and not completed count. I already tried but failed.
SQL Fiddle:
http://sqlfiddle.com/#!9/c8dab/1
Question:
How can I get completed and not completed count by extending below query?
Query:
SELECT keyworddefs.name as sprint,
SUM(CASE WHEN bugs.bug_status IN ('CLOSED', 'VERIFIED')
THEN bugs.cf1 + bugs.cf2
ELSE 0 END) completed,
SUM(bugs.cf1 + bugs.cf2) total,
(CASE WHEN SUM(CASE WHEN bugs.bug_status IN ('CLOSED', 'VERIFIED')
THEN bugs.cf1 + bugs.cf2
ELSE 0 END)=SUM(bugs.cf1 + bugs.cf2)
THEN 'Completed'
ELSE 'Not Completed' END) status
FROM bugs
JOIN keywords ON bugs.bug_id = keywords.bug_id
JOIN keyworddefs ON keyworddefs.id = keywords.keywordid
GROUP BY keywords.keywordid
ORDER BY keyworddefs.name DESC;
Output:
+--------+------------------------+--------------------+---------------+
| name | completed | total | status |
+--------+------------------------+--------------------+---------------+
| K2 | 14 | 14 | Completed |
| J2 | 16 | 24 | Not Completed |
| J1 | 0 | 5 | Not Completed |
+--------+------------------------+--------------------+---------------+
After extending query, expected output just:
+---------------+--------+
| status | count |
+------------------------+
| Completed | 1 |
| Not Completed | 2 |
+------------------------+
As Strawberry said, give it a derived table name.
Schema:
-- bugs table1 (master table) :
CREATE TABLE `bugs`
( `bug_id` int(11) NOT NULL,
`bug_date` date NOT NULL,
`cf1` int(11) NOT NULL,
`cf2` int(11) NOT NULL,
`bug_status` varchar(200) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `bugs` (`bug_id`, `bug_date`, `cf1`, `cf2`, `bug_status`) VALUES
(101, '2016-07-19', 3, 2, 'RESOLVED'),
(102, '2016-07-19', 2, 1, 'VERIFIED'),
(103, '2016-07-19', 2, 1, 'VERIFIED'),
(103, '2016-07-19', 2, 1, 'VERIFIED'),
(1363, '2016-07-19', 2, 1, 'VERIFIED'),
(1352, '2016-07-19', 2, 1, 'VERIFIED'),
(102, '2016-07-19', 2, 1, 'VERIFIED'),
(102, '2016-07-22', 2, 2, 'CLOSED'),
(103, '2016-07-22', 2, 2, 'CLOSED'),
(103, '2016-07-22', 2, 2, 'CLOSED'),
(102, '2016-07-19', 3, 2, 'NEW'),
(102, '2016-07-19', 2, 1, 'REOPENED'),
(102, '2016-07-19', 2, 1, 'CLOSED'),
(102, '2016-07-19', 2, 1, 'VERIFIED'),
(1363, '2016-07-19', 2, 1, 'VERIFIED'),
(1352, '2016-07-19', 2, 1, 'VERIFIED'),
(565, '2016-07-19', 2, 1, 'VERIFIED'),
(398, '2016-07-22', 2, 2, 'CLOSED'),
(565, '2016-07-22', 2, 2, 'CLOSED'),
(9872, '2016-07-22', 2, 2, 'CLOSED');
-- keywords table2 (having keyword ids):
CREATE TABLE `keywords`
( `bug_id` int(11) NOT NULL,
`keywordid` varchar(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `keywords` (`bug_id`, `keywordid`) VALUES
(101, '1'), (102, '2'), (103, '3'), (104, '4'), (105, '1'), (106, '1'), (107, '2'), (108, '3'), (109, '4');
-- keyworddefs table3 (having keyword names according to keywordid):
CREATE TABLE `keyworddefs`
( `id` int(11) NOT NULL,
`name` varchar(200) NOT NULL,
`description` varchar(200) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `keyworddefs` (`id`, `name`, `description`) VALUES
(1, 'J1', 'My J1 item'), (2, 'J2', 'My J2 item'), (3, 'K2', 'My K2 item'), (4, 'K2', 'My K2 item');
Your query:
SELECT keyworddefs.name as sprint,
SUM(CASE WHEN bugs.bug_status IN ('CLOSED', 'VERIFIED')
THEN bugs.cf1 + bugs.cf2
ELSE 0 END) completed_story_points,
SUM(bugs.cf1 + bugs.cf2) total_story_points,
(CASE WHEN SUM(CASE WHEN bugs.bug_status IN ('CLOSED', 'VERIFIED')
THEN bugs.cf1 + bugs.cf2
ELSE 0 END)=SUM(bugs.cf1 + bugs.cf2)
THEN 'Completed'
ELSE 'Not Completed' END) sprint_status
FROM bugs
JOIN keywords ON bugs.bug_id = keywords.bug_id
JOIN keyworddefs ON keyworddefs.id = keywords.keywordid
GROUP BY keywords.keywordid
ORDER BY keyworddefs.name DESC;
Your Output:
+--------+------------------------+--------------------+---------------+
| sprint | completed_story_points | total_story_points | sprint_status |
+--------+------------------------+--------------------+---------------+
| K2 | 14 | 14 | Completed |
| J2 | 16 | 24 | Not Completed |
| J1 | 0 | 5 | Not Completed |
+--------+------------------------+--------------------+---------------+
Wanted:
+---------------+--------+
| status | count |
+------------------------+
| Completed | 1 |
| Not Completed | 2 |
+------------------------+
Lazy approach (meaning, without much thought):
SELECT sprint_status,count(*) AS count
FROM
(
SELECT keyworddefs.name as sprint,
SUM(CASE WHEN bugs.bug_status IN ('CLOSED', 'VERIFIED')
THEN bugs.cf1 + bugs.cf2
ELSE 0 END) completed_story_points,
SUM(bugs.cf1 + bugs.cf2) total_story_points,
(CASE WHEN SUM(CASE WHEN bugs.bug_status IN ('CLOSED', 'VERIFIED')
THEN bugs.cf1 + bugs.cf2
ELSE 0 END)=SUM(bugs.cf1 + bugs.cf2)
THEN 'Completed'
ELSE 'Not Completed' END) sprint_status
FROM bugs
JOIN keywords ON bugs.bug_id = keywords.bug_id
JOIN keyworddefs ON keyworddefs.id = keywords.keywordid
GROUP BY keywords.keywordid
) xDerived
GROUP BY sprint_status
ORDER BY sprint_status;
Output:
+---------------+-------+
| sprint_status | count |
+---------------+-------+
| Completed | 1 |
| Not Completed | 2 |
+---------------+-------+
Order by whatever
Every derived table requires a name. The above wrapped chunk is one.
So we chose the name xDerived. It is not used by name thereafter (though it could have been). But it still needs a name or an Error will occur.

Mysql Grouping of Max field by month on a set of Fields

I would like to generate a SQL to list to display the maximum tally count based on enumerated group of values on a monthly basis. As this would be useful for analytics based algorithm in displaying the total impressions of a particular data type.
Please check my sample table:
CREATE TABLE IF NOT EXISTS `company_attendance_tally` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`start_date` datetime NOT NULL,
`sick_type` enum('VACATION','SICK','MATERNITY') COLLATE utf8_unicode_ci NOT NULL,
`leave_count` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `start_date` (`start_date`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=32 ;
--
-- Dumping data for table `company_attendance_tally`
--
INSERT INTO `company_attendance_tally` (`id`, `start_date`, `sick_type`, `leave_count`) VALUES
(1, '2013-03-01 16:58:44', 'VACATION', 5),
(2, '2013-03-15 10:44:35', 'SICK', 43),
(3, '2013-03-21 17:03:33', 'MATERNITY', 44),
(4, '2013-03-07 23:01:30', 'MATERNITY', 10),
(5, '2013-03-22 17:07:07', 'MATERNITY', 1),
(6, '2013-03-08 19:33:04', 'VACATION', 40),
(7, '2013-03-17 12:27:00', 'MATERNITY', 15),
(8, '2013-03-03 23:26:48', 'SICK', 11),
(9, '2013-03-05 02:16:37', 'MATERNITY', 41),
(10, '2013-03-20 12:04:28', 'MATERNITY', 18),
(11, '2013-03-18 02:10:00', 'MATERNITY', 1),
(12, '2013-03-03 09:47:02', 'MATERNITY', 19),
(13, '2013-03-22 10:17:52', 'MATERNITY', 25),
(14, '2013-03-03 19:41:52', 'VACATION', 10),
(15, '2013-03-02 19:28:41', 'SICK', 39),
(16, '2013-03-01 20:45:26', 'SICK', 42),
(17, '2013-03-26 23:52:16', 'MATERNITY', 29),
(18, '2013-03-29 14:10:58', 'SICK', 44),
(19, '2013-03-27 03:11:40', 'MATERNITY', 12),
(20, '2013-03-06 18:38:28', 'MATERNITY', 30),
(21, '2013-03-07 20:49:14', 'VACATION', 27),
(22, '2013-03-13 11:38:45', 'VACATION', 14),
(23, '2013-03-02 19:13:31', 'SICK', 2),
(24, '2013-03-01 10:08:18', 'SICK', 27),
(25, '2013-03-20 01:56:38', 'VACATION', 3),
(26, '2013-03-04 21:02:05', 'SICK', 7),
(27, '2013-03-17 00:47:17', 'MATERNITY', 36),
(28, '2013-03-04 08:12:56', 'VACATION', 5),
(29, '2013-03-18 08:50:57', 'SICK', 34),
(30, '2013-03-26 02:20:58', 'VACATION', 20),
(31, '2013-03-27 10:27:00', 'SICK', 21);
http://sqlfiddle.com/#!2/bbd1e3
I would like to display a similar output below based on the above scenario:
month| day | sick_type | leave_count |
-----------------------------------------------------
3| 08 | VACATION | 40
3| 29 | SICK | 29
3| 21 | MATERNITY | 44
and so on so forth...
4| ... | MATERNITY | ..
4| ... | SICK | ..
4| ... | VACATION | ..
5| ... | MATERNITY | ..
5| ... | SICK | ..
5| ... | VACATION | ..
If I understand correctly what you want you can do following by leveraging non-standard MySQL GROUP BY extension
SELECT MONTH(start_date) month,
DAYOFMONTH(start_date) day,
sick_type,
leave_count
FROM
(
SELECT start_date, sick_type, leave_count
FROM company_attendance_tally
WHERE start_date >= '2013-01-01'
AND start_date < '2014-01-01'
ORDER BY MONTH(start_date), sick_type, leave_count DESC
) q
GROUP BY MONTH(start_date), sick_type
Note: Month values alone (without a year values) in the resultset make sense only if you limit the resultset by one year boundaries (see WHERE clause).
Output:
| MONTH | DAY | SICK_TYPE | LEAVE_COUNT |
|-------|-----|-----------|-------------|
| 3 | 8 | VACATION | 40 |
| 3 | 29 | SICK | 44 |
| 3 | 21 | MATERNITY | 44 |
Here is SQLFiddle demo
Use this:
SELECT DAY( start_date ) AS d,
MONTH( start_date ) AS mon,
sick_type,
MAX( leave_count ) AS leave_count
FROM `company_attendance_tally`
GROUP BY mon, sick_type
If you want to do it 'by-the-book', consider the following...
SELECT x.*
FROM company_attendance_tally x
JOIN
( SELECT MONTH(start_date) start_month
, sick_type
, MAX(leave_count) max_leave_count
FROM company_attendance_tally
GROUP
BY MONTH(start_date)
, sick_type
) y
ON y.start_month = MONTH(x.start_date)
AND y.sick_type = x.sick_type
AND y.max_leave_count = x.leave_count;