Take data on SQL when statement is change - mysql

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;

Related

Count with join, subquery and group by

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);

Date split in Mysql based on rate info in table

The system is a hotel management software with multiple hotels attached to it. The schema is as follows:
CREATE TABLE `ms_property` (
`id` int(10) NOT NULL,
`name` varchar(254) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `ms_property` (`id`, `name`) VALUES(1, 'Black Forest');
CREATE TABLE `ms_property_room` ( `id` int(10) NOT NULL, `property_id` int(10) NOT NULL,
`room_name` varchar(254) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `ms_property_room` (`id`, `property_id`, `room_name`) VALUES (1, 1, 'Standard Room'),
(2, 1, 'AC Room');
CREATE TABLE `ms_tariff_type` (
`tt_id` bigint(20) NOT NULL,
`tt_tariff_name` text
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `ms_tariff_type` (`tt_id`,`tt_tariff_name`) VALUES
(1, 'Season Rates'),
(2, 'Contracted Rates');
CREATE TABLE `room_tariff` (
`id` bigint(20) NOT NULL ,
`room_id` bigint(20) ,
`tariff_type_id` bigint(20) ,
`tariff_from` date,
`tariff_to` date,
`single_rate` int(11),
`default_rate` int(11)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `room_tariff` (`id`, `room_id`,`tariff_type_id`,`tariff_from`, `tariff_to`, `single_rate`, `default_rate`) VALUES
(1, 1, 1, '2019-01-01', '2019-01-20',1000,2000),
(2, 1, 2, '2019-02-06', '2019-02-12',5000,10000),
(3, 2, 1, '2019-03-05', '2019-04-10',8000,7000);
CREATE TABLE `tariff_hike_day` (
`id` bigint(20) NOT NULL,
`room_id` bigint(20) ,
`tariff_type_id` bigint(20) ,
`hd_tariff_from` date,
`hd_tariff_to` date,
`hd_single_rate` int(11),
`hd_default_rate` int(11),
`thd_sunday` smallint(6) COMMENT 'Is rate applicable on Sunday 1=>yes 0=>no',
`thd_monday` smallint(6) COMMENT 'Is rate applicable on Monday 1=>yes 0=>no',
`thd_thuesday` smallint(6) COMMENT 'Is rate applicable on Tuesday 1=>yes 0=>no',
`thd_wednesday` smallint(6) COMMENT 'Is rate applicable on Wednesday 1=>yes 0=>no',
`thd_thursday` smallint(6) COMMENT 'Is rate applicable on Thursday 1=>yes 0=>no',
`thd_friday` smallint(6) COMMENT 'Is rate applicable on Friday 1=>yes 0=>no',
`thd_saturday` smallint(6) COMMENT 'Is rate applicable on Saturday 1=>yes 0=>no'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `tariff_hike_day` (`id`, `room_id`, `tariff_type_id`,`hd_tariff_from`, `hd_tariff_to`, `hd_single_rate`, `hd_default_rate`, `thd_sunday`, `thd_monday`, `thd_thuesday`, `thd_wednesday`, `thd_thursday`, `thd_friday`, `thd_saturday`) VALUES
(1, 1, 1, '2019-01-05', '2019-01-10',100,200, 1, 1, 1, 1, 1, 1, 1),
(1, 2, 1, '2019-03-09', '2019-03-25',400,600, 1, 0, 0, 1, 0, 0, 0);
The scenario is to display the room rates applicable to hotels based on rate info provided in two tables. Normally a room will have different types of rate like "Contracted Rates", "Seasonal Rates" etc and in each type, Hotel Administrative Team will provide the applicable rates and the date range in which the rates are applicable.
The problem arises when the Hotel Administrative Team wants to specify additional hikes which are applicable on certain days. This information is stored in tariff_hike_day table where the Hotel Administrative Team can specify the date range and the days (sunday, monday etc) on which the hike is to be applied on base rate.
When the full entry is completed, the system is expected to display the result as follows:
+-------+---------------+---------------+------------------+------------+------------+-------------+--------------+
| Sl No | Property Name | Room | Tariff Type | Date From | Date To | Single Rate | Default Rate |
+-------+---------------+---------------+------------------+------------+------------+-------------+--------------+
| 1 | Black Forest | Standard Room | Season Rates | 2019-01-01 | 2019-01-04 | 1000 | 2000 |
| 2 | Black Forest | Standard Room | Season Rates | 2019-01-05 | 2019-01-10 | 1100 | 2200 |
| 3 | Black Forest | Standard Room | Season Rates | 2019-01-11 | 2019-01-20 | 1000 | 2000 |
| 4 | Black Forest | Standard Room | Contracted Rates | 2019-02-06 | 2019-02-12 | 5000 | 10000 |
| 5 | Black Forest | AC Room | Season Rates | 2019-03-05 | 2019-03-09 | 8000 | 7000 |
| 6 | Black Forest | AC Room | Season Rates | 2019-03-10 | 2019-03-10 | 8400 | 8600 |
| 7 | Black Forest | AC Room | Season Rates | 2019-03-11 | 2019-03-12 | 8000 | 7000 |
| 8 | Black Forest | AC Room | Season Rates | 2019-03-13 | 2019-03-13 | 8400 | 8600 |
| 9 | Black Forest | AC Room | Season Rates | 2019-03-14 | 2019-03-16 | 8000 | 7000 |
| 10 | Black Forest | AC Room | Season Rates | 2019-03-17 | 2019-03-17 | 8400 | 8600 |
| 11 | Black Forest | AC Room | Season Rates | 2019-03-18 | 2019-03-19 | 8000 | 7000 |
| 12 | Black Forest | AC Room | Season Rates | 2019-03-20 | 2019-03-20 | 8400 | 8600 |
| 13 | Black Forest | AC Room | Season Rates | 2019-03-21 | 2019-03-23 | 8000 | 7000 |
| 14 | Black Forest | AC Room | Season Rates | 2019-03-24 | 2019-03-24 | 8400 | 8600 |
| 15 | Black Forest | AC Room | Season Rates | 2019-03-25 | 2019-04-10 | 8000 | 7000 |
+-------+---------------+---------------+------------------+------------+------------+-------------+--------------+
Any help would be appreciated.
I know bit late to answer but hope it would help you.
First of all you need to make sure that there is no overlapped dates for same room with same tariff on both tables 'room_tariff' and 'tariff_hike_day'
To find it you can use the queries given below.
Finding Duplicate Dates(Overlapped dates) in room_tariff Table
SELECT
a.*
FROM
`room_tariff` AS a
INNER JOIN `room_tariff` AS b
ON a.`id` != b.`id`
AND a.`room_id` = b.`room_id`
AND a.`tariff_type_id` = b.`tariff_type_id`
AND NOT (
(
a.`tariff_from` > b.`tariff_from`
AND a.`tariff_from` > b.`tariff_to`
)
OR (
a.`tariff_to` < b.`tariff_from`
AND a.`tariff_to` < b.`tariff_to`
)
)
GROUP BY a.`room_id`,
a.`tariff_type_id`,
a.`tariff_from`,
a.`tariff_to`
ORDER BY a.`room_id` ASC,
a.`tariff_type_id` ASC,
a.`tariff_from` ASC ;
Finding Duplicate Dates(Overlapped dates) in tariff_hike_day Table
SELECT
a.*
FROM
`tariff_hike_day` AS a
INNER JOIN `tariff_hike_day` AS b
ON a.`id` != b.`id`
AND a.`room_id` = b.`room_id`
AND a.`tariff_type_id` = b.`tariff_type_id`
AND NOT (
(
a.`hd_tariff_from` > b.`hd_tariff_from`
AND a.`hd_tariff_from` > b.`hd_tariff_to`
)
OR (
a.`hd_tariff_to` < b.`hd_tariff_from`
AND a.`hd_tariff_to` < b.`hd_tariff_to`
)
)
GROUP BY a.`room_id`,
a.`tariff_type_id`,
a.`hd_tariff_from`,
a.`hd_tariff_to`
ORDER BY a.`room_id` ASC,
a.`tariff_type_id` ASC,
a.`hd_tariff_from` ASC ;
Both queries should return 'ZERO' rows to avoid over lapping. Here i joined same table and checking overlapped dates for same room with same tariff.
This link will help you get more explanation
To get result as you expected, We can do with the help of Stored Procedure as follows.
DELIMITER $$
DROP PROCEDURE IF EXISTS `testprocedure`$$
CREATE PROCEDURE `testprocedure`()
BEGIN
DECLARE my_id,
my_room_id,
my_tariff_type_id,
my_hd_id BIGINT ;
DECLARE my_single_rate,
my_default_rate,
my_hd_single_rate,
my_hd_default_rate INT ;
DECLARE my_tariff_from,
my_tariff_to,
my_hd_tariff_from,
my_hd_tariff_to,
currentdate,
startdate,
stopdate DATE ;
DECLARE my_thd_sunday,
my_thd_monday,
my_thd_tuesday,
my_thd_wednesday,
my_thd_thursday,
my_thd_friday,
my_thd_saturday SMALLINT ;
DECLARE cur_done INTEGER DEFAULT 0 ;
DECLARE `should_rollback` BOOL DEFAULT FALSE;
DECLARE cur1 CURSOR FOR
SELECT
a1.*,
a2.id,
hd_tariff_from,
hd_tariff_to,
hd_single_rate,
hd_default_rate,
thd_sunday,
thd_monday,
thd_thuesday,
thd_wednesday,
thd_thursday,
thd_friday,
thd_saturday
FROM
`room_tariff` AS a1
LEFT JOIN `tariff_hike_day` a2
ON a1.`room_id` = a2.`room_id`
AND a1.`tariff_type_id` = a2.`tariff_type_id`
AND a2.`hd_tariff_from` != '0000-00-00'
AND NOT (
a1.`tariff_from` > a2.`hd_tariff_to`
OR a1.`tariff_to` < a2.`hd_tariff_from`
)
WHERE a1.tariff_from != '0000-00-00'
AND a1.`tariff_from` <= a1.`tariff_to`
ORDER BY a1.`room_id` ASC,
a1.`tariff_type_id` ASC,
a1.`tariff_from` ASC,
a2.`hd_tariff_from` ASC ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET cur_done = 1 ;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET `should_rollback` = TRUE;
START TRANSACTION;
CREATE TABLE IF NOT EXISTS `room_rate_temp` (
`id` INT (11) UNSIGNED NOT NULL AUTO_INCREMENT,
`room_id` BIGINT (20) NOT NULL,
`tariff_type_id` BIGINT (20) NOT NULL,
`tariff_from` DATE NOT NULL,
`tariff_to` DATE NOT NULL,
`single_rate` INT (11) NOT NULL,
`default_rate` INT (11) NOT NULL,
`resultset_id` INT (11) UNSIGNED NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8 ;
SET #last_res_id := 0 ;
TRUNCATE TABLE room_rate_temp ;
OPEN cur1 ;
loop_matched_tables :
LOOP
FETCH cur1 INTO my_id,
my_room_id,
my_tariff_type_id,
my_tariff_from,
my_tariff_to,
my_single_rate,
my_default_rate,
my_hd_id,
my_hd_tariff_from,
my_hd_tariff_to,
my_hd_single_rate,
my_hd_default_rate,
my_thd_sunday,
my_thd_monday,
my_thd_tuesday,
my_thd_wednesday,
my_thd_thursday,
my_thd_friday,
my_thd_saturday ;
IF cur_done = 1 THEN
CLOSE cur1 ;
LEAVE loop_matched_tables ;
END IF ;
IF my_tariff_from <= my_tariff_to THEN
IF #last_res_id = my_id THEN
SELECT id,tariff_from FROM `room_rate_temp` WHERE `resultset_id` = my_id ORDER BY id DESC LIMIT 1 INTO #lastid,#last_tariff_from ;
SET my_tariff_from := #last_tariff_from ;
DELETE FROM room_rate_temp WHERE id = #lastid ;
END IF ;
IF my_hd_id IS NULL THEN
INSERT INTO room_rate_temp
VALUES
(
NULL,
my_room_id,
my_tariff_type_id,
my_tariff_from,
my_tariff_to,
my_single_rate,
my_default_rate,
my_id
) ;
ELSE
IF ( my_hd_tariff_from <= my_hd_tariff_to ) THEN
SET startdate := my_tariff_from ;
SET currentdate := my_tariff_from ;
SET stopdate := my_tariff_to ;
SET #insflag := 1 ;
SET #last_insid := #last_hike_flag := #hiketablecovered := #splitonce := 0 ;
WHILE
currentdate <= stopdate DO
SET #my_repeat_col_name := DAYNAME(currentdate) ;
SET #hd_single_rate := my_single_rate ;
SET #hd_default_rate := my_default_rate ;
SELECT
CASE
#my_repeat_col_name
WHEN 'Sunday'
THEN my_thd_sunday
WHEN 'Monday'
THEN my_thd_monday
WHEN 'Tuesday'
THEN my_thd_tuesday
WHEN 'Wednesday'
THEN my_thd_wednesday
WHEN 'Thursday'
THEN my_thd_thursday
WHEN 'Friday'
THEN my_thd_friday
WHEN 'Saturday'
THEN my_thd_saturday
ELSE NULL
END AS mydate INTO #hikeapplicable ;
IF ( currentdate BETWEEN my_hd_tariff_from AND my_hd_tariff_to ) THEN
IF ( #last_hike_flag != #hikeapplicable ) THEN
SET #insflag := 1 ;
SET #last_hike_flag := #hikeapplicable ;
SET #splitonce := 1 ;
IF ( #hikeapplicable = 1 ) THEN
SET #hd_single_rate := my_single_rate + my_hd_single_rate ;
SET #hd_default_rate := my_default_rate + my_hd_default_rate ;
END IF ;
END IF ;
SET #hiketablecovered := 1;
ELSEIF ( (currentdate > my_hd_tariff_to) AND ( #hiketablecovered = 1 ) AND (#splitonce = 1) ) THEN
IF(#last_hike_flag = 1) THEN
SET #insflag := 1;
END IF ;
SET #hiketablecovered := #splitonce := 0 ;
END IF ;
IF (#insflag = 1) THEN
INSERT INTO room_rate_temp VALUES ( NULL, my_room_id, my_tariff_type_id, currentdate, currentdate, #hd_single_rate, #hd_default_rate, my_id );
SET #last_insid := LAST_INSERT_ID() ;
SET #insflag := 0 ;
ELSE
UPDATE room_rate_temp SET tariff_to = currentdate WHERE id = #last_insid;
END IF ;
SET currentdate = ADDDATE(currentdate, INTERVAL 1 DAY) ;
END WHILE ;
END IF ;
END IF ;
SET #last_res_id := my_id;
END IF ;
END LOOP loop_matched_tables ;
SET #count:=0;
SELECT (#count:=#count+1) AS `Sl No`, d.name AS `Property Name`, c.room_name AS Room, b.tt_tariff_name AS `Tariff Type`, a.tariff_from AS `Date From`, a.tariff_to AS `Date To`, a.single_rate AS `Single Rate`, a.default_rate AS `Default Rate`
FROM room_rate_temp AS a INNER JOIN ms_tariff_type AS b ON a.tariff_type_id = b.tt_id INNER JOIN ms_property_room AS C
ON a.room_id = c.id INNER JOIN ms_property AS d ON c.property_id = d.id;
IF `should_rollback` THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END$$
DELIMITER ;
In this procedure,
For storing the result, I created one temp table and will exist until next query so that you can fetch last result at any time.
First i joined tariff and hike table to find the matching for similar date range.
Then looping the query result and breaks rows when hike is applicable.

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')
;

How to divide the current price to the last price in the same column in sql

How can I divide ((89-95)/95)*100 or ((95-100)/100)*100
CREATE TABLE `priceindex` (
`priceIndexId` int(11) NOT NULL,
`Date` date DEFAULT NULL,
`Price` decimal(31,9) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
priceIndexId | Date | Price | Currentvalue/previous value
1 | 2017-11-30 | 100 |
2 | 2017-12-06 | 95 | answer should be(95-100)/100)*100 = -0.50
3 | 2017-12-13 | 89 | answer should be(89-95)/95)*100 = -0.63
Thanks in advance
You need to get the previous value. One method uses a correlated subquery. I would suggest using a subquery for the calculation:
select pi.priceIndexId, pi.Date, pi.Price,
(pi.Price - pi.prev_price) / pi.prev_price as change
from (select pi.*,
(select pi2.price
from priceindex pi2
where pi2.date < pi.date
order by pi2.date desc
limit 1
) as prev_price
from priceindex pi
) pi;

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;