JOIN 3表和(LIMIT 2行ORDER BY时间DESC)

mad*_*net 5 mysql join sql-order-by limit

我知道这个问题已被多次询问,但我无法实现它.
我做了一个减少的例子,因此很容易重现.
我想加入3个表但是在最后一个表上我想限制为2行DESC

CREATE TABLE `cars` (
`car_id` int(11) NOT NULL AUTO_INCREMENT,
`plate` varchar(10) NOT NULL,
`km` int(11) NOT NULL,
`status` tinyint(1) NOT NULL,
PRIMARY KEY (`car_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
INSERT INTO `cars` (`car_id`, `plate`, `km`, `status`) VALUES
(1, 'ABC1234', 130123, 1),
(2, 'DEF1234', 100123, 1),
(3, 'QWE1234', 5000, 1),
(4, 'ASD1234', 3000, 1),
(5, 'ZXC1234', 23000, 0);
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE `cars_to_users` (
`car_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
UNIQUE KEY `car_id` (`car_id`,`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `cars_to_users` (`car_id`, `user_id`) VALUES
(1, 1),
(2, 1),
(3, 2),
(4, 2),
(5, 2);
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE `service` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`car_plate` varchar(10) NOT NULL,
`s_timestamp` int(10) NOT NULL,
`price` double NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=111 ;
INSERT INTO `service` (`id`, `car_plate`, `s_timestamp`, `price`) VALUES
(1, 'ABC1234', 1312300100, 30),
(2, 'DEF1234', 1312300100, 15),
(3, 'QWE1234', 1312300100, 16),
(4, 'ASD1234', 1312300100, 50),
(5, 'ABC1234', 1312300200, 50),
(6, 'DEF1234', 1312300200, 25),
(7, 'QWE1234', 1312300200, 30),
(8, 'ABC1234', 1312300300, 20),
(9, 'ASD1234', 1312300300, 60),
(10, 'ABC1234', 1312300400, 15),
(11, 'ASD1234', 1312300400, 20);
Run Code Online (Sandbox Code Playgroud)

我想要的是这个

car_id  plate  km  car_plate  s_timestamp  price  
3 QWE1234 5000 QWE1234 1312300200 30 
3 QWE1234 5000 QWE1234 1312300100 16 
4 ASD1234 3000 ASD1234 1312300400 20 
4 ASD1234 3000 ASD1234 1312300300 60 
Run Code Online (Sandbox Code Playgroud)

来自s_timestamp DESC排序的user_id = 2的每辆车的"service"表中的2行

ORDER BY s_timestamp LIMIT 2 DESC
Run Code Online (Sandbox Code Playgroud)

我尝试这个查询,但给了我"服务"的所有行

SELECT ctu.user_id, c.car_id, c.plate, c.km, s.car_plate, s.s_timestamp, s.price
FROM cars_to_users ctu 
LEFT JOIN cars c ON  ctu.car_id = c.car_id
LEFT JOIN service s ON c.plate = s.car_plate 
WHERE ctu.user_id = '2' 
AND c.status = 1
Run Code Online (Sandbox Code Playgroud)

如果我添加"GROUP BY c.car_id"我每辆车只能获得1排而不是我想要的2排

我尝试了很多查询,但没有得到我想要的.

需要记住的是,"服务"表的行数超过900万行,数据量超过示例,并且会增长.

ace*_*ace 2

这个答案相当复杂。我不确定它在您的数据库上的性能如何。

SELECT ctu.user_id, c.car_id, c.plate, c.km, s.car_plate, s.s_timestamp, s.price
FROM cars_to_users ctu 
LEFT JOIN cars c ON  ctu.car_id = c.car_id
LEFT JOIN service s ON c.plate = s.car_plate
JOIN 
(
  SELECT service.car_plate,max(service.s_timestamp) as s_timestamp
  FROM service
  JOIN 
  (
    SELECT car_plate, max(s_timestamp) as s_timestamp FROM service GROUP BY car_plate
  ) as max_timestamp ON max_timestamp.car_plate = service.car_plate AND service.s_timestamp < max_timestamp.s_timestamp
  GROUP BY service.car_plate
) as max_2_timestamp ON s.car_plate = max_2_timestamp.car_plate AND s.s_timestamp >= max_2_timestamp.s_timestamp
WHERE ctu.user_id = '2' 
AND c.status = 1
ORDER BY s_timestamp DESC
Run Code Online (Sandbox Code Playgroud)


我想你可以像这样首先将 2 个子查询放入临时表中

DROP TABLE IF EXISTS max_timestamp;
DROP TABLE IF EXISTS max_2_timestamp;

CREATE TEMPORARY table max_timestamp SELECT car_plate, max(s_timestamp) as s_timestamp FROM service GROUP BY car_plate;

CREATE TEMPORARY table max_2_timestamp
(
  SELECT service.car_plate,max(service.s_timestamp) as s_timestamp
  FROM service
  JOIN max_timestamp ON max_timestamp.car_plate = service.car_plate AND service.s_timestamp < max_timestamp.s_timestamp
  GROUP BY service.car_plate
);

SELECT ctu.user_id, c.car_id, c.plate, c.km, s.car_plate, s.s_timestamp, s.price
FROM cars_to_users ctu 
LEFT JOIN cars c ON  ctu.car_id = c.car_id
LEFT JOIN service s ON c.plate = s.car_plate
JOIN max_2_timestamp ON s.car_plate = max_2_timestamp.car_plate AND s.s_timestamp >= max_2_timestamp.s_timestamp
WHERE ctu.user_id = '2' 
AND c.status = 1
ORDER BY s_timestamp DESC;
Run Code Online (Sandbox Code Playgroud)


编辑:另一种选择

您将只有一个查询,但我无法检查它在您的系统中是否足够有效。让我们创建一个函数来检索每个中的

第二个最近的s_timestampcar_plateService

CREATE FUNCTION LatestService (car_plate varchar(10))
RETURNS int(10)
RETURN 
(SELECT s_timestamp
FROM service s
WHERE s.car_plate=`car_plate`
ORDER BY s.s_timestamp desc
LIMIT 1,1);
Run Code Online (Sandbox Code Playgroud)

然后您可以使用该函数执行查询。

SELECT ctu.user_id, c.car_id, c.plate, c.km, s.car_plate, s.s_timestamp, s.price
FROM cars_to_users ctu 
LEFT JOIN cars c ON  ctu.car_id = c.car_id
LEFT JOIN service s ON c.plate = s.car_plate 
WHERE ctu.user_id = '2' 
AND c.status = 1
AND s.s_timestamp >= LatestService(s.car_plate);
Run Code Online (Sandbox Code Playgroud)