2011-08-05 11 views
5

Tôi biết câu hỏi này đã được hỏi nhiều lần, nhưng tôi gặp sự cố khi triển khai nó.
Tôi đã thực hiện một ví dụ cắt giảm để dễ tái sản xuất.
Tôi muốn tham gia 3 bảng nhưng trên người cuối cùng tôi muốn giới hạn đến 2 hàng DESC
THAM GIA 3 bảng và (GIỚI HẠN 2 hàng ĐẶT HÀNG THEO D timeI 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); 

Những gì tôi muốn là này

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 hàng từ "s ervice "bảng cho mỗi chiếc xe của user_id = 2 lệnh của s_timestamp DESC

ORDER BY s_timestamp LIMIT 2 DESC 

tôi cố gắng truy vấn này nhưng mang lại cho tôi tất cả các hàng từ "dịch vụ"

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 

Nếu tôi thêm" GROUP BY c .car_id "Tôi chỉ nhận được 1 hàng cho mỗi xe và không phải 2 Tôi muốn

Tôi thử nhiều truy vấn nhưng không nhận được những gì tôi muốn.

Điều cần lưu ý là bảng "dịch vụ" có hơn 9 triệu hàng và nhiều dữ liệu hơn ví dụ và lớn lên.

+0

phiên bản mysql mà bạn đang sử dụng như bạn đã nói giới hạn không hoạt động trên phiên bản của bạn là gì ??? – reggie

+0

Bạn có thể sử dụng các câu lệnh của tôi hay không, sử dụng các bảng tạm thời nên làm cho nó nhanh hơn một chút. – ace

+0

@ace Giải pháp của bạn hoạt động rất tốt. Nhưng nếu cùng một điều có thể đạt được thông qua một truy vấn phụ và số lượng hàng được lấy ra trong bảng truy vấn con/temp chỉ là 2, tại sao không sử dụng truy vấn phụ thay thế. – reggie

Trả lời

2

Câu trả lời này là khá phức tạp. Và tôi không chắc nó sẽ hoạt động tốt như thế nào trên cơ sở dữ liệu của bạ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 
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 


Tôi đoán bạn có thể đặt 2 phụ truy vấn trong một bảng temp đầu tiên như

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; 


EDIT này: Một Alternative

Bạn sẽ chỉ có một truy vấn nhưng tôi không thể kiểm tra xem đó là hiệu quả đủ trong hệ thống của bạn.

Hãy tạo các chức năng đó sẽ lấy được nhiều nhất 2 gần đây s_timestamp cho mỗi car_plate trong 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); 

Sau đó, bạn có thể thực hiện truy vấn bằng cách sử dụng chức năng.

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

Phần phức tạp (có thể là ngu ngốc?) Trong truy vấn của tôi là lấy dấu thời gian 2_max cho mỗi car_plate. Vì tôi không thể nghĩ ra một cách để làm điều này vào lúc này, tôi đã phải chạy tối đa (dấu thời gian) hai lần, với dấu hai chấm (dấu thời gian) thứ hai nhỏ hơn dấu trừ thứ nhất (dấu thời gian). Khi bạn có dấu thời gian tối đa thứ 2, bạn có thể tạo truy vấn. Cách của tôi nhận được kết quả là rất khủng khiếp nếu bạn có kế hoạch để có được đầu 3 hoặc nhiều hơn. :) – ace

+0

Công việc đầu tiên như tôi muốn trong ví dụ, nhưng trong bảng thực tế (9mil-hàng) mất khoảng 35 giây để tuân thủ - vì vậy nó nằm ngoài phạm vi. Nếu tôi lấy car_plates từ bảng fisrt 2 và tạo vòng lặp foreach php cho mỗi tấm như (SELECT * FROM service WHERE car_plate = '$ plate' ORDER BY timestamp_3 DESC LIMIT 2) Tôi có khoảng 5 giây để than phiền cho 4 xe, NHƯNG Tôi không muốn thực hiện 4 + 1 truy vấn nhưng 1 truy vấn và tôi tin rằng 5 giây có thể được tối ưu hóa quá. – madnet

+0

Việc tạo bảng tạm thời có quá lâu không? – ace

0

thay

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

với

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) 

Tôi biết điều này sẽ làm việc trong MS Sql Server, không chắc chắn 100% về mysql.

(EDIT) Và như bình luận dưới đây, nó không. Cách giải quyết là một chút xấu xí (nhưng không phải là quá xấu) và được mô tả ở đây:

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

+0

Nó có lỗi này trong mysql 5.1.51 'Mã lỗi: 1235 Phiên bản này của MySQL chưa hỗ trợ 'LIMIT & IN/ALL/ANY/SOME subquery'' – ace

+0

Nice thử nhưng không hoạt động. Lỗi như @ace nói – madnet

+0

Sau khi đăng bài mà tôi đã xem tài liệu tham khảo để xem nó có được hay không được hỗ trợ hay không, và cũng thấy rằng nó không phải. Có thể có một cách giải quyết khác. Tôi sẽ chỉnh sửa nếu có một thứ không quá phức tạp. – hatchet