2009-02-12 25 views
18

Tôi có hai bảng: 'phim' và 'người dùng'. Có mối quan hệ n: m giữa những người, mô tả những phim mà người dùng đã xem. Điều này được mô tả với một bảng 'nhìn thấy' Bây giờ tôi muốn tìm ra cho một người dùng nhất định, tất cả các bộ phim ông đã không nhìn thấy. Giải pháp hiện tại của tôi là như sau:MySQL: Tìm các hàng không tham gia vào mối quan hệ

SELECT * 
FROM movies 
WHERE movies.id NOT IN (
    SELECT seen.movie_id 
    FROM seen 
    WHERE seen.user_id=123 
) 

Điều này làm việc tốt nhưng có vẻ không quy mô rất tốt. Có cách tiếp cận tốt hơn cho điều này?

+0

Nếu nó không mở rộng, thì việc lập chỉ mục của bạn không hiệu quả. Chỉ số của bạn là gì? – dkretz

+0

> Điều này hoạt động tốt nhưng dường như không quy mô rất tốt. Có cách tiếp cận tốt hơn cho điều này? Bạn đã thử EXPLAIN về truy vấn này chưa? – VolkerK

Trả lời

27

Dưới đây là một cách điển hình để thực hiện truy vấn này mà không sử dụng phương pháp truy vấn con bạn đã hiển thị. Điều này có thể đáp ứng yêu cầu của @ Godeke để xem giải pháp dựa trên tham gia.

SELECT * 
FROM movies m 
LEFT OUTER JOIN seen s 
ON (m.id = s.movie_id AND s.user_id = 123) 
WHERE s.movie_id IS NULL; 

Tuy nhiên, trong hầu hết các thương hiệu của cơ sở dữ liệu, giải pháp này có thể hoạt động kém hơn giải pháp truy vấn phụ. Tốt nhất là sử dụng GIẢI THÍCH để phân tích cả hai truy vấn, để xem câu hỏi nào sẽ làm tốt hơn cho lược đồ và dữ liệu của bạn.

Đây là một biến thể của giải pháp subquery:

SELECT * 
FROM movies m 
WHERE NOT EXISTS (SELECT * FROM seen s 
        WHERE s.movie_id = m.id 
        AND s.user_id=123); 

Đây là một subquery tương quan, mà phải được đánh giá cho mỗi hàng của truy vấn bên ngoài. Thông thường điều này là tốn kém, và truy vấn ví dụ ban đầu của bạn là tốt hơn. Mặt khác, trong MySQL "NOT EXISTS" thường tốt hơn "column NOT IN (...)"

Một lần nữa, bạn phải kiểm tra từng giải pháp và so sánh kết quả để chắc chắn. Đó là một sự lãng phí thời gian để chọn bất kỳ giải pháp mà không đo lường hiệu suất.

+0

Tôi chỉ cần quên đi điều này 'OUTER JOIN' lừa. –

4

Truy vấn của bạn không chỉ hoạt động, đó là cách tiếp cận đúng cho vấn đề như đã nêu. Có lẽ bạn có thể tìm thấy một cách khác để tiếp cận vấn đề? Một LIMIT đơn giản trên lựa chọn bên ngoài của bạn nên rất nhanh ngay cả đối với các bảng lớn, chẳng hạn.

4

Đã xem là bảng tham gia của bạn, vì vậy, có vẻ như đây là giải pháp đúng. Bạn có hiệu quả "trừ" tập hợp các ID phim trong SEEN (đối với người dùng) từ tổng số trong MOVIES, dẫn đến các bộ phim không nhìn thấy cho người dùng đó.

Điều này được gọi là "tham gia tiêu cực", và đáng buồn là KHÔNG CÓ hoặc KHÔNG tồn tại là những lựa chọn tốt nhất. (Tôi rất thích nhìn thấy một cú pháp tham gia tiêu cực tương tự như kết nối INNER/OUTER/LEFT/RIGHT, nhưng khi mệnh đề ON có thể là một câu lệnh trừ).

@ Giải pháp của Bill không có truy vấn con nên hoạt động, mặc dù khi ông lưu ý rằng bạn nên kiểm tra giải pháp của mình để có hiệu suất theo cả hai cách. Tôi nghi ngờ rằng subquery hay không, toàn bộ chỉ số SEEN.ID (và tất nhiên toàn bộ chỉ số MOVIE.ID) sẽ được đánh giá theo cả hai cách: nó sẽ phụ thuộc vào cách trình tối ưu hóa xử lý nó từ đó.

0

Nếu DBMS của bạn hỗ trợ các chỉ mục bitmap, bạn có thể thử chúng.

+0

Ông đã gắn thẻ câu hỏi 'mysql'. MySQL không hỗ trợ các chỉ mục bitmap. –

+0

Rất tiếc, tôi không xem xét thẻ. :( –

Các vấn đề liên quan