Count with join, subquery and group by - mysql

I have this table:
CREATE TABLE `logs` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`visitor_id` INT(11) NOT NULL,
`date_time` DATETIME NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `info` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`id_no` INT(11) NOT NULL,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `info`
VALUES
(1,20, 'vip'),(2,21, 'customer'),(3,22,'vip')
,(4,23, 'customer'),(5,24, 'vip'),(6,30,'customer')
,(7,31, 'vip'),(8,32,'customer'),(9,33,'vip' ),(10,34, 'vip'),(11,35,'vip');
INSERT INTO `logs`
VALUES
(1,20, '2019-01-01 08:00:00'),(2,21, '2019-01-01 08:05:00'),(3,22,'2019-01-01 08:08:00')
,(4,23, '2019-01-01 08:10:00'),(5,24, '2019-01-01 08:15:00'),(6,30,'2019-01-02 09:00:00')
,(7,31, '2019-01-02 09:10:00'),(8,32,'2019-01-02 09:15:00'),(9,33,'2019-01-02 09:17:00' ),(10,34, '2019-01-02 09:18:00');
This query:
select date(l.date_time) as `date`, (select count(distinct(l.visitor_id)) from `logs` l join info i on (i.id_no = l.visitor_id) where i.`name` = 'CUSTOMER' and l.visitor_id=i.id_no) as total_customer, (select count(l.visitor_id) from `logs` l join info i on (i.id_no = l.visitor_id) where i.`name` = 'vip') as total_vip, count(distinct(l.visitor_id)) as total from `logs` l join info i on (i.id_no = l.visitor_id) where l.date_time between '2019-01-01 00:00:00' and '2019-01-02 23:00:00' group by date(l.date_time);
has this result:
| date | total_customer | total_vip | total |
-------------------------------------------------------
| 2019-01-01 | 4 | 6 | 5 |
| 2019-01-02 | 4 | 6 | 5 |
my desired result is this:
| date | total_customer | total_vip | total |
-------------------------------------------------------
| 2019-01-01 | 2 | 3 | 5 |
| 2019-01-02 | 2 | 3 | 5 |
May I know what's wrong with my query? I'm using mysql 5.5. Thank you.

You don't need subqueries, you can use sum() case
select date(l.date_time) as date
, sum(case when i.name = 'customer' then 1 else 0 end) as customers
, sum(case when i.name = 'vip' then 1 else 0 end) as visitors
, count(1) as total
from logs l
join info i on (i.id_no = l.visitor_id)
where l.date_time between '2019-01-01 00:00:00' and '2019-01-02 23:00:00'
group by date(l.date_time);

Related

Mysql 5.6 timestampdiff problem with return result

I have two tables
CREATE TABLE `contract` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`emp_id` int(11) DEFAULT NULL ,
`sign_time` datetime DEFAULT NULL ,
`end_time` datetime DEFAULT NULL ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
CREATE TABLE `employee_detail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL ,
`stage` varchar(100) DEFAULT NULL ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
and some data:
INSERT INTO `contract` (`id`, `emp_id`,`sign_time`, `end_time`) VALUES
('25', '83', '2018-11-21 00:00:00', '2018-12-01 15:27:00');
INSERT INTO `contract` (`id`, `emp_id`,`sign_time`, `end_time`) VALUES
('26', '94', '2018-11-21 00:00:00', '2018-12-01 15:23:00');
INSERT INTO `employee_detail` (`id`, `name`, `stage`) VALUES ('83', 'Michael',
'1');
INSERT INTO `employee_detail` (`id`, `name`, `stage`) VALUES ('94', 'John',
'1');
when I query the database with SQL:
SELECT
c.*
FROM
contract c
JOIN employee_detail e ON c.emp_id = e.id
WHERE
e.stage != - 1
AND (
TIMESTAMPDIFF(
MINUTE,
'2018-11-30 09:18:23',
c.end_time
)
) > 0
AND TIMESTAMPDIFF(
MONTH,
'2018-11-30 09:18:23',
c.end_time
) = 0
I got 0 records.
But if I query with SQL:
SELECT
c.*
FROM
contract c
JOIN employee_detail e ON c.emp_id = e.id
WHERE
e.stage != - 1
AND (
TIMESTAMPDIFF(
MINUTE,
'2018-11-30 09:18:23',
c.end_time
)
) > '0'
AND TIMESTAMPDIFF(
MONTH,
'2018-11-30 09:18:23',
c.end_time
) = '0'
which turns interger 0 to string '0', I got two right records.
I searched from https://dev.mysql.com/doc/refman/5.6/en/date-and-time-functions.html#function_timestampdiff, and I found that:
Returns datetime_expr2 − datetime_expr1, where datetime_expr1 and datetime_expr2 are date or datetime expressions. One expression may be a date and the other a datetime; a date value is treated as a datetime having the time part '00:00:00' where necessary. The unit for the result (an integer) is given by the unit argument. The legal values for unit are the same as those listed in the description of the TIMESTAMPADD() function.
I'm confused about the result and the explanation of Oracle.
So timestampdiff function returns one integer value but when I use it in SQL statement I got an incorrect result while if I treated it as string value I got the right answer. Could anybody explain the weird phenomenon? Thanks a lot!
You should not check if record withing specified time range like this as MySQL will not use indexes in this case. Try to alter const part and compare with column. Something like this:
WHERE
e.stage != - 1
AND c.end_time < DATE_SUB('2018-11-30 09:18:23', INTERVAL 1 MINUTE)
AND c.end_time > DATE_SUB('2018-11-30 09:18:23', INTERVAL 1 MONTH)
I get inconsistent results depending on which predicate is used first in the query:
SELECT
c.*
, TIMESTAMPDIFF(MONTH,'2018-11-30 09:18:23',c.end_time) diff_month1
, case when TIMESTAMPDIFF(MONTH,'2018-11-30 09:18:23',c.end_time) = 0 then 'equal'
when TIMESTAMPDIFF(MONTH,'2018-11-30 09:18:23',c.end_time) > 0 then 'greater'
end diff_month
, TIMESTAMPDIFF(MINUTE,'2018-11-30 09:18:23',c.end_time) diff_minute1
, case when TIMESTAMPDIFF(MINUTE,'2018-11-30 09:18:23',c.end_time) = 0 then 'equal'
when TIMESTAMPDIFF(MINUTE,'2018-11-30 09:18:23',c.end_time) > 0 then 'greater'
end diff_minute
FROM contract c
JOIN employee_detail e ON c.emp_id = e.id
where TIMESTAMPDIFF(MONTH ,'2018-11-30 09:18:23',c.end_time) = 0
and TIMESTAMPDIFF(MINUTE,'2018-11-30 09:18:23',c.end_time) = 0
id | emp_id | sign_time | end_time | diff_month1 | diff_month | diff_minute1 | diff_minute
-: | -----: | :------------------ | :------------------ | ----------: | :--------- | -----------: | :----------
25 | 83 | 2018-11-21 00:00:00 | 2018-12-01 15:27:00 | 0 | equal | 1808 | greater
26 | 94 | 2018-11-21 00:00:00 | 2018-12-01 15:23:00 | 0 | equal | 1804 | greater
SELECT
c.*
, TIMESTAMPDIFF(MONTH,'2018-11-30 09:18:23',c.end_time) diff_month1
, case when TIMESTAMPDIFF(MONTH,'2018-11-30 09:18:23',c.end_time) = 0 then 'equal'
when TIMESTAMPDIFF(MONTH,'2018-11-30 09:18:23',c.end_time) > 0 then 'greater'
end diff_month
, TIMESTAMPDIFF(MINUTE,'2018-11-30 09:18:23',c.end_time) diff_minute1
, case when TIMESTAMPDIFF(MINUTE,'2018-11-30 09:18:23',c.end_time) = 0 then 'equal'
when TIMESTAMPDIFF(MINUTE,'2018-11-30 09:18:23',c.end_time) > 0 then 'greater'
end diff_minute
FROM contract c
JOIN employee_detail e ON c.emp_id = e.id
where TIMESTAMPDIFF(MINUTE,'2018-11-30 09:18:23',c.end_time) = 0
and TIMESTAMPDIFF(MONTH ,'2018-11-30 09:18:23',c.end_time) = 0
id | emp_id | sign_time | end_time | diff_month1 | diff_month | diff_minute1 | diff_minute
-: | -----: | :-------- | :------- | ----------: | :--------- | -----------: | :----------
db<>fiddle here
This trial is in MySQL 8.0, but I get the same inconsistency in MySQL 5.7.12 using rextester.com

SQL performance issue : find a route

I'm struggling with performance issue on my of my SQL query
I have a train journey traveling 5 stations named "A - B - C - D - E".
A passenger book a ticket for only "B - C - D" ride.
I need to retrieve all stations my passengers goes to.
What I have stored :
JOURNEY
+----+--------------------+-------------------+-------------------+-----------------+
| id | departure_datetime | arrival_datetime | departure_station | arrival_station |
+----+--------------------+-------------------+-------------------+-----------------+
| 1 | 2018-01-01 06:00 | 2018-01-01 10:00 | A | E |
+----+--------------------+-------------------+-------------------+-----------------+
BOOKING
+----+------------+-------------------+-----------------+
| id | journey_id | departure_station | arrival_station |
+----+------------+-------------------+-----------------+
| 1 | 1 | B | D |
+----+------------+-------------------+-----------------+
LEG
+----+------------+-------------------+-----------------+------------------+------------------+
| id | journey_id | departure_station | arrival_station | departure_time | arrival_time |
+----+------------+-------------------+-----------------+------------------+------------------+
| 1 | 1 | A | B | 2018-01-01 06:00 | 2018-01-01 07:00 |
| 2 | 1 | B | C | 2018-01-01 07:00 | 2018-01-01 08:00 |
| 3 | 1 | C | D | 2018-01-01 08:00 | 2018-01-01 09:00 |
| 4 | 1 | D | E | 2018-01-01 09:00 | 2018-01-01 10:00 |
+----+------------+-------------------+-----------------+------------------+------------------+
Only way I found to retrieve stations is :
select b.id as booking, l.departure_station, l.arrival_station
from JOURNEY j
inner join BOOKING b on j.id = b.journey_id
inner join LEG dl on (j.id = dl.journey_id and b.departure_station = dl.departure_station)
inner join LEG al on (j.id = al.journey_id and b.arrival_station = al.arrival_station)
inner join LEG l on (j.id = l.journey_id and l.departure_time >= dl.departure_time and l.arrival_time <= al.arrival_time)
where b.id = 1
But my LEG table is huge and doing this 3 joins on is very slow. Is there a way I can join only one time LEG table to increase performance ?
Intended return :
+------------+-------------------+-----------------+
| booking_id | departure_station | arrival_station |
+------------+-------------------+-----------------+
| 1 | B | C |
| 1 | C | D |
+------------+-------------------+-----------------+
I work on mariadb 12.2 so i have access to window function but i'm still not very comfortable with it.
Thanks.
EDIT : create tables :
CREATE TABLE `BOOKING` (
`id` INT(11) NOT NULL,
`journey_id` INT(11) NULL DEFAULT NULL,
`departure_station` VARCHAR(50) NULL DEFAULT NULL,
`arrival_station` VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `JOURNEY` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`departure_time` DATETIME NULL DEFAULT NULL,
`arrival_time` DATETIME NULL DEFAULT NULL,
`departure_station` VARCHAR(50) NULL DEFAULT NULL,
`arrival_station` VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `LEG` (
`id` INT(11) NOT NULL,
`journey_id` INT(11) NULL DEFAULT NULL,
`departure_station` VARCHAR(50) NULL DEFAULT NULL,
`arrival_station` VARCHAR(50) NULL DEFAULT NULL,
`departure_time` DATETIME NULL DEFAULT NULL,
`arrival_time` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`)
);
I don't like your DB schema.
But in your particular case, since you have your query working good for you.
I would just create few indexes too speed up execution.
In general there is nothing wrong when you need to join table few times to itself.
http://sqlfiddle.com/#!9/1a467/1
Try just add 4 indexes:
CREATE INDEX journey ON BOOKING (journey_id);
CREATE INDEX arrival ON LEG (journey_id, arrival_station);
CREATE INDEX departure ON LEG (journey_id, departure_station);
CREATE INDEX d_a_time ON LEG (journey_id, departure_time, arrival_time);
And run your query again, it should be much faster when using indexes.
I would suggest using Common Table Expression (CTE):
WITH leg_cte as
(
SELECT l.* FROM leg l
JOIN booking b
ON l.journey_id = b.journey_id
WHERE b.id = 1
)
SELECT
b.id as booking,
l.departure_station,
l.arrival_station
FROM
booking b
JOIN leg_cte dl
ON b.departure_station = dl.departure_station
JOIN leg_cte al
ON b.arrival_station = al.arrival_station
JOIN leg_cte l
ON l.departure_time >= dl.departure_time AND l.arrival_time <= al.arrival_time
WHERE b.id = 1
Try it left join and use REGEXP to filiter departure_station and arrival_station
select T3.id booking_id , T1.departure_station,T1.arrival_station
from LEG T1
left join JOURNEY T2 on T1.`journey_id` = T2.`id`
and (T1.`departure_time` >= T2.`departure_datetime` and T1.`arrival_time` <= T2.`arrival_datetime`)
left join BOOKING T3 on T3.`id` = T2.`id`
and T1.departure_station REGEXP (CONCAT('[',T3.departure_station , '-' , T3.arrival_station,']' ))
and T1.arrival_station REGEXP (CONCAT('[',T3.departure_station , '-' , T3.arrival_station,']' ))
where T1.journey_id = 1 and T3.id is not null ;
SQL Fiddle Demo Link
| booking_id | departure_station | arrival_station |
|------------|-------------------|-----------------|
| 1 | B | C |
| 1 | C | D |
Test DDL:
CREATE TABLE JOURNEY
(`id` int, `departure_datetime` datetime, `arrival_datetime` datetime, `departure_station` varchar(1), `arrival_station` varchar(1))
;
INSERT INTO JOURNEY
(`id`, `departure_datetime`, `arrival_datetime`, `departure_station`, `arrival_station`)
VALUES
(1, '2018-01-01 06:00:00', '2018-01-01 10:00:00', 'A', 'E')
;
CREATE TABLE BOOKING
(`id` int, `journey_id` int, `departure_station` varchar(1), `arrival_station` varchar(1))
;
INSERT INTO BOOKING
(`id`, `journey_id`, `departure_station`, `arrival_station`)
VALUES
(1, 1, 'B', 'D')
;
CREATE TABLE LEG
(`id` int, `journey_id` int, `departure_station` varchar(1), `arrival_station` varchar(1), `departure_time` datetime, `arrival_time` datetime)
;
INSERT INTO LEG
(`id`, `journey_id`, `departure_station`, `arrival_station`, `departure_time`, `arrival_time`)
VALUES
(1, 1, 'A', 'B', '2018-01-01 06:00:00', '2018-01-01 07:00:00'),
(2, 1, 'B', 'C', '2018-01-01 07:00:00', '2018-01-01 08:00:00'),
(3, 1, 'C', 'D', '2018-01-01 08:00:00', '2018-01-01 09:00:00'),
(4, 1, 'D', 'E', '2018-01-01 09:00:00', '2018-01-01 10:00:00')
;

Mysql join two tables sum, where and group by

I have two tables in the following structure:
sales
|date |time | name | total |
|2017-04-01 |10:23:59 | aaa | 100 |
|2017-04-01 |10:23:59 | aaa | 150 |
|2017-04-01 |11:33:30 | bbb | 200 |
|2017-04-01 |11:33:30 | bbb | 120 |
|2017-04-02 |10:50:59 | aaa | 70 |
|2017-04-02 |10:30:59 | bbb | 35 |
payment
|date |time | name | amount |
|2017-04-01 |10:23:59 | aaa | 300 |
|2017-04-01 |11:33:30 | bbb | 400 |
|2017-04-02 |10:50:59 | aaa | 425 |
|2017-04-02 |10:30:59 | bbb | 600 |
Terms
sales.time = payment.time
where date = 2017-04-01
sum(sales.total) and sum(payment.amount)
group by time
i want this result
|date |time | name | sum(total) | sum(amount)|
|2017-04-01 |13:23:59 | aaa | 250 | 300 |
|2017-04-01 |12:33:30 | bbb | 320 | 400 |
Table structure
CREATE TABLE `payment` (`id` int(5) NOT NULL,`date` date NOT NULL,`time` time NOT NULL,`name` varchar(10) NOT NULL,`amount` varchar(10) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
INSERT INTO `payment` (`id`, `date`, `time`, `name`, `amount`) VALUES(3, '2017-04-01', '10:23:59', 'aaa', '300'),(4, '2017-04-01', '11:33:30', 'bbb', '400'),(5, '2017-04-02', '10:50:59', 'aaa', '425'),(6, '2017-04-02', '10:30:59', 'bbb', '600');
CREATE TABLE `sales` ( `id` int(5) NOT NULL,`date` date NOT NULL,`time` time NOT NULL,`name` varchar(10) NOT NULL,`total` int(10) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `sales` (`id`, `date`, `time`, `name`, `total`) VALUES(1, '2017-04-01', '10:23:59', 'aaa', 100),(2, '2017-04-01', '10:23:59', 'aaa', 150),(3, '2017-04-01', '11:33:30', 'bbb', 200),(4, '2017-04-01', '11:33:30', 'bbb', 120),(5, '2017-04-02', '10:50:59', 'aaa', 70),(6, '2017-04-02', '10:50:59', 'bbb', 35);
ALTER TABLE `payment` ADD PRIMARY KEY (`id`);
ALTER TABLE `sales` ADD PRIMARY KEY (`id`);
ALTER TABLE `payment` MODIFY `id` int(5) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;
ALTER TABLE `sales` MODIFY `id` int(5) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;
Query
SELECT sales.time,sales.name,
SUM(sales.total),SUM(payment.amount)
FROM sales,payment
WHERE sales.time=payment.time
and sales.date like '2017-04-01%'
GROUP BY sales.time
ORDER BY sales.time;
Result is
10:23:59 aaa 250 600
11:33:30 bbb 320 800
You are multiplying the payments amount with the number of sales records, because you are joining all payments records with all sales records before summing up the amounts.
Aggregate first, and only join then.
In case there can always only be one payments record per date, time and name:
select p.name, p.time, p.name, s.sales_total, p.amount
from payments p
join
(
select date, time, name, sum(total) as total
from sales
group by date, time, name
) s
on s.date = p.date and s.time = p.time and s.name = p.name
where p.date = date '2017-04-01';
Otherwise:
select p.name, p.time, p.name, s.total, p.amount
(
select date, time, name, sum(amount) as amount
from payments
group by date, time, name
) p
join
(
select date, time, name, sum(total) as total
from sales
group by date, time, name
) s
on s.date = p.date and s.time = p.time and s.name = p.name
where p.date = date '2017-04-01';

select from tables with different numbers of rows

I'm hoping there is a simple answer to this. Competitors race over a series of 3 races. Some competitors only show up for one race. How could I show a final result for ALL competitors?
race 1
+------+--------+
| name | result |
+------+--------+
| Ali | 30 |
| Bob | 28 |
| Cal | 26 |
+------+--------+
race 2
+------+--------+
| name | result |
+------+--------+
| Ali | 32 |
| Bob | 31 |
| Dan | 24 |
+------+--------+
race 3
+------+--------+
| name | result |
+------+--------+
| Eva | 23 |
| Dan | 25 |
+------+--------+
The final result should look like this:
+------+--------+--------+--------+
| name | result | result | result |
+------+--------+--------+--------+
| Ali | 30 | 32 | |
| Bob | 28 | 31 | |
| Cal | 26 | | |
| Dan | | 24 | 25 |
| Eva | | | 23 |
+------+--------+--------+--------+
The problem I have is with ordering by name from multiple tables.
Here is the example data:
CREATE TABLE race (name varchar(20), result int);
CREATE TABLE race1 LIKE race;
INSERT INTO race1 VALUES ('Ali', '30'), ('Bob', '28'), ('Cal', '26');
CREATE TABLE race2 like race;
insert INTO race2 VALUES ('Ali', '32'), ('Bob', '31'), ('Dan', '24');
CREATE TABLE race3 LIKE race;
INSERT INTO race3 VALUES ('Eva', '23'), ('Dan', '25');
Many thanks!
Here we go !!!
select race1.name as name, race1.result, race2.result, race3.result from race1
left join race2 on race2.name = race1.name
left join race3 on race3.name = race1.name
union
select race2.name as name, race1.result, race2.result, race3.result from race2
left join race1 on race1.name = race2.name
left join race3 on race3.name = race2.name
union
select race3.name as name, race1.result, race2.result, race3.result from race3
left join race1 on race1.name = race3.name
left join race2 on race2.name = race3.name;
It is working :)
select s.name,
max(case when s.R = 'Result1' then s.result else '' end) as result1,
max(case when s.R = 'Result2' then s.result else '' end) as result2,
max(case when s.R = 'Result3' then s.result else '' end) as result3
from
(
select 'Result1' as R,r1.* from race1 r1
union all
select 'Result2' as R,r2.* from race2 r2
union all
select 'Result3' as R,r3.* from race3 r3
) s
group by s.name
result
+------+---------+---------+---------+
| name | result1 | result2 | result3 |
+------+---------+---------+---------+
| Ali | 30 | 32 | |
| Bob | 28 | 31 | |
| Cal | 26 | | |
| Dan | | 24 | 25 |
| Eva | | | 23 |
+------+---------+---------+---------+
5 rows in set (0.00 sec)
I personally would create the schema in a different way.
One table for the users, one for the races and one that connects both:
-- Create syntax for TABLE 'races'
CREATE TABLE `races` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Create syntax for TABLE 'users'
CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Create syntax for TABLE 'race_results'
CREATE TABLE `race_results` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`race_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`result` int(11) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Let's insert some data (should be equal to your data set).
-- Insert data
INSERT INTO users (name)values('Ali'),('Bob'),('Cal'),('Dan'), ('Eva');
INSERT INTO races (name)values('Race1'),('Race2'),('Race3');
INSERT INTO race_results (user_id, race_id, result)values(1,1,30),(2,1,30),(1,2,28),(2,2,31),(3,1,26),(4,2,24),(4,3,25),(5,3,23);
Then you could write the query like this:
-- Static version
SELECT us.name, sum(if(ra.name='Race1', result, null)) as Race1, sum(if(ra.name='Race2', result, null)) as Race2, sum(if(ra.name='Race3', result, null)) as Race3
FROM race_results as rr
LEFT JOIN users as us on us.id = rr.user_id
LEFT JOIN races as ra on ra.id = rr.race_id
GROUP BY us.id;
Which gives you the result you're looking for. (I changed the column names to make it more obvious which result belongs to which race.)
But I've to admit that this works fine for 3 races but what if you have 30 or more?
Here is a more dynamic version of the above query, which kind of creates itself ;)
-- Dynamic version
SET #sql = '';
SELECT
#sql := CONCAT(#sql,if(#sql='','',', '),temp.output)
FROM
(SELECT
CONCAT("sum(if(ra.name='", race.name, "', result, null)) as ", race.name) as output
FROM races as race
) as temp;
SET #sql = CONCAT("SELECT us.name,", #sql, " FROM race_results as rr LEFT JOIN users as us on us.id = rr.user_id LEFT JOIN races as ra on ra.id = rr.race_id GROUP BY 1;");
SELECT #sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Take data on SQL when statement is change

Let says i've data like below.
+-------+-------+
| time | status |
+-------+-------+
| 01:00 | On |
| 02:00 | On |
| 03:00 | On |
| 04:00 | Off |
| 05:00 | On |
| 06:00 | On |
| 07:00 | Off |
| 08:00 | Off |
| 09:00 | On |
| 10:00 | On |
| 11:00 | Off |
+-------+-------+
My expected result table is
+-------+-------+
| On | Off |
+-------+-------+
| 01:00 | 04:00 |
| 05:00 | 07:00 |
| 09:00 | 11:00 |
+-------+-------+
How to create query like to become my expected result ?, because when i cannot use decode into this case.
this is my create table command
CREATE TABLE `gps_data` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`imei_no` int(255) DEFAULT NULL,
`car_identification_no` varchar(255) DEFAULT NULL,
`datetime` bigint(10) DEFAULT NULL,
`longitude` float(10,7) DEFAULT NULL,
`latitude` float(10,7) DEFAULT NULL,
`engine_status` varchar(100) DEFAULT NULL,
`speed` int(100) DEFAULT NULL,
`mileage` bigint(255) DEFAULT NULL,
`alarm` int(20) DEFAULT NULL,
`address` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `car_identification_no` (`car_identification_no`)
) ENGINE=MyISAM AUTO_INCREMENT=430564 DEFAULT CHARSET=latin1
i want to use datetime and engine_status (on/off)
Thanks
Try this:
SELECT MIN(CASE WHEN (grp - 1) MOD 2 + 1 = 1 THEN `time` END) AS 'On',
MIN(CASE WHEN (grp - 1) MOD 2 + 1 = 2 THEN `time` END) AS 'Off'
FROM (
SELECT `time`, `status`,
#grp := IF(#prev_status = `status`, #grp,
IF(#prev_status := `status`, #grp + 1, #grp + 1)) AS grp
FROM mytable
CROSS JOIN (SELECT #grp := 0, #prev_status = '') AS vars
ORDER BY `time`) AS t
GROUP BY (grp - 1) DIV 2
Demo here
Explanation:
The inner query:
SELECT `time`, `status`,
#grp := IF(#prev_status = `status`, #grp,
IF(#prev_status := `status`, #grp + 1, #grp + 1)) AS grp
FROM mytable
CROSS JOIN (SELECT #grp := 0, #prev_status = '') AS vars
ORDER BY `time`
uses variables in order to generate the derived table seen below:
# time, status, grp
======================
01:00:00, On, 1
02:00:00, On, 1
03:00:00, On, 1
04:00:00, Off, 2
05:00:00, On, 3
06:00:00, On, 3
07:00:00, Off, 4
08:00:00, Off, 4
09:00:00, On, 5
10:00:00, On, 5
11:00:00, Off, 6
So #grp identifies consecutive records having the same status value.
The outer query groups by grp integer divided by 2. This essentially groups together consecutive On - Off pairs. Finally, using conditional aggregation we can get in the same SELECT both On, Off values.
SELECT t1.t "On", MIN(t2.`time`) "Off"
FROM (
SELECT `datetime` t, #stat := engine_status s
FROM gps_data JOIN (SELECT #stat='') vars
WHERE engine_status != #stat) t1
JOIN gps_data t2 ON t1.t < t2.`datetime`
WHERE t1.s = 'On' AND t2.engine_status = 'Off'
GROUP BY t1.t;