ПРИСОЕДИНЯЙТЕСЬ К 3 таблицам и (ОГРАНИЧИВАЙТЕ 2 строки Порядок по времени DESC) - PullRequest
5 голосов
/ 05 августа 2011

Я знаю, что этот вопрос задавался много раз, но у меня возникают проблемы с его реализацией.
Я сделал урезанный пример, чтобы его было легко воспроизвести.
Я хочу объединить 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);

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

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

Что я хочу, это

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 

2 строки изтаблица "service" для каждой машины user_id = 2, упорядоченная s_timestamp DESC

ORDER BY s_timestamp LIMIT 2 DESC

Я пытаюсь выполнить этот запрос, но выдает все строки из "service"

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

Еслидобавить "GROUP BY c.car_id". Я получаю только 1 строку на машину, а не 2. Я хочу

Я пробую много запросов, но не получаю то, что хочу.

Следует иметь в виду, чточто таблица "service" содержит более 9 миллионов строк и больше данных, чем в примере, и растет.

Ответы [ 3 ]

2 голосов
/ 05 августа 2011

Этот ответ довольно сложный.И я не уверен, насколько хорошо он будет работать в вашей базе данных.

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


Полагаю, вы можете сначала поместить 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;


РЕДАКТИРОВАТЬ : другая альтернатива

У вас будет только один запрос, но я не могу проверить, достаточно ли эффективного вваша система.

Давайте создадим функцию, которая будет извлекать 2-ую самую последнюю s_timestamp для каждого car_plate в Service

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

Затем вы можете выполнить запрос, используя функцию.

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);
0 голосов
/ 05 августа 2011

заменить

LEFT JOIN service s ON c.plate = s.car_plate 

на

LEFT JOIN service s on s.id 
  in (SELECT s2.id FROM service s2
      WHERE s2.car_plate=c.plate
      ORDER BY s2.s_timestamp DESC LIMIT 2)

Я знаю, что это будет работать в MS Sql Server, не уверен на 100% насчет mysql.

(EDIT) Икак прокомментировано ниже, это не так.Обходной путь немного уродлив (но не слишком плох) и описан здесь:

http://forums.mysql.com/read.php?10,416311,416461#msg-416461

0 голосов
/ 05 августа 2011

Сортировка по некоторому идентификатору и добавление 'HAVING COUNT (some_id) <2' </p>

http://www.java2s.com/Code/SQL/Select-Clause/UseCOUNTGROUPandHAVING.htm

...