2011-08-05 13 views
5

Sé que esta pregunta se ha hecho muchas veces, pero estoy teniendo problemas para implementarla.
He hecho un ejemplo de reducción por lo que es fácil de reproducir.
Quiero unirme a 3 mesas pero en el último que quieren limitar a 2 filas DESC
UNIR 3 tablas y (LIMITAR 2 filas ORDEN POR tiempo 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); 

Lo que quiero es este

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 filas de "s ervicio "mesa para todos los coches de la user_id = 2 ordenado por DESC s_timestamp

ORDER BY s_timestamp LIMIT 2 DESC 

de que este intento consulta pero me da todas las filas de "servicio"

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 

Si agrego" GROUP BY c .car_id "Solo obtengo 1 fila por auto y no 2 Quiero

Intento muchas consultas pero no consigo lo que quiero.

Una cosa a tener en cuenta es que el "servicio" de la tabla tiene más de 9 millones de filas y más datos que el ejemplo, y crece.

+0

¿Cuál es la versión de mysql que está utilizando como dijo el límite no funciona en su versión ??? – reggie

+0

¿Pudiste usar mis declaraciones, usar tablas temporales debería hacerlo un poco más rápido? – ace

+0

@ace Su solución también funciona muy bien. Pero si se puede lograr lo mismo a través de una consulta secundaria y el número de filas que se recuperarán en la tabla de subconsulta/temperatura es solo 2, ¿por qué no utilizar la subconsulta en su lugar? – reggie

Respuesta

2

Esta respuesta es bastante complicado. Y no estoy seguro de qué tan bien funcionará en su base de datos.

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 


Creo que se puede poner las 2 consultas sub en una tabla temporal en primer lugar como éste

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; 


EDITAR: Otra alternativa

que sólo tendrá una consulta pero no puedo verificar si es eficiente suficiente en su sistema.

Vamos a crear la función que va a recuperar el segundo más reciente s_timestamp para cada car_plate en 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); 

A continuación, puede realizar la consulta utilizando la función.

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

La parte complicada (¿tal vez estúpida?) En mi consulta es obtener la marca de tiempo 2nd_max para cada car_plate. Como no puedo pensar en una forma de hacerlo en este momento, tuve que ejecutar max (timestamp) dos veces, con el segundo máximo (timestamp) menor que el 1st max (timestamp). Una vez que tenga la segunda marca de tiempo máxima, puede generar la consulta. Mi forma de obtener el conjunto de resultados es muy horrible si planeas obtener los 3 mejores o más. :) – ace

+0

El primero funciona como yo quiero en el ejemplo, pero en la tabla de la vida real (9mil-rows) toma alrededor de 35 segundos para que se complique, por lo que está fuera del alcance. Si tomo car_plates de las dos tablas fisrt y hago un loop fore de php para cada placa como (SELECCIONE * FROM servicio DONDE car_plate = '$ plate' ORDER BY timestamp_3 DESC LIMIT 2) Tengo alrededor de 5 segundos para 4 autos, PERO No quiero hacer 4 + 1 consultas sino 1 consulta y creo que los 5secs también se pueden optimizar. – madnet

+0

¿Generar las tablas temporales lleva demasiado tiempo también? – ace

0

reemplazar

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

con

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) 

Sé que esto va a funcionar en MS Sql Server, no 100% seguro acerca de mysql.

(EDITAR) Y como se comenta a continuación, no es así. La solución es un poco feo (pero no demasiado malo) y se describe aquí:

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

+0

Tiene este error en mysql 5.1.51 'Código de error: 1235 Esta versión de MySQL aún no admite 'LIMIT & IN/ALL/ANY/ALGUNA subconsulta'' – ace

+0

Nice intenta pero no trabaja Error como @ace decir – madnet

+0

Después de publicar que estaba mirando la referencia para ver si era compatible o no, y ver también que no es así. Puede haber una solución. Editaré si hay uno que no sea prohibitivamente complicado. – hatchet

Cuestiones relacionadas