2011-07-19 35 views
8

Tôi có cơ sở dữ liệu MySQL trong đó bảng A có mối quan hệ một-nhiều với bảng B và tôi muốn chọn tất cả các hàng trong bảng B không có con trong bảng A. tôi đã cố gắng sử dụngChọn hàng cha mẹ chỉ khi nó không có con

SELECT id FROM A WHERE NOT EXISTS (SELECT * FROM B WHERE B.id=A.id) 

SELECT id FROM A LEFT JOIN B ON A.id=B.id WHERE B.id IS NULL 

Cả hai dường như chậm. Có truy vấn nhanh hơn để đạt được điều tương tự không?

Trong trường hợp điều này có liên quan, trong bảng cơ sở dữ liệu của tôi A có khoảng 500.000 hàng và bảng B có khoảng 3 đến 4 triệu hàng.

Edit: Đối với các bảng cơ sở dữ liệu thực tế trong tôi, giải thích cho tôi:

+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+ 
| id | select_type  | table   | type | possible_keys | key      | key_len | ref | rows | Extra     | 
+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+ 
| 1 | PRIMARY   | frontend_form471 | index | NULL   | frontend_form471_61a633e8 | 32  | NULL | 671927 | Using where; Using index | 
| 2 | DEPENDENT SUBQUERY | SchoolData  | index | PRIMARY  | PRIMARY     | 49  | NULL | 3121110 | Using where; Using index | 
+----+--------------------+------------------+-------+---------------+---------------------------+---------+------+---------+--------------------------+ 

cho

select number from frontend_form471 where not exists (select * from SchoolData where SchoolData.`f471 Application Number`=frontend_form471.number) 

+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+ 
| id | select_type | table   | type | possible_keys | key      | key_len | ref | rows | Extra           | 
+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+ 
| 1 | SIMPLE  | frontend_form471 | index | NULL   | frontend_form471_61a633e8 | 32  | NULL | 671927 | Using index; Using temporary     | 
| 1 | SIMPLE  | SchoolData  | index | PRIMARY  | PRIMARY     | 49  | NULL | 3121110 | Using where; Using index; Not exists; Distinct | 
+----+-------------+------------------+-------+---------------+---------------------------+---------+------+---------+------------------------------------------------+ 

cho

select distinct number from frontend_form471 left join SchoolData on frontend_form471.number=SchoolData.`f471 Application Number` where SchoolData.`f471 Application Number` is NULL 

nơi trong trường hợp frontend_form471 của tôi là bảng A và SchoolData là bảng B

Edit2: Trong bảng B (SchoolData) trong cơ sở dữ liệu của tôi, id là phần đầu tiên của một khóa chính hai phần, vì vậy nó là được lập chỉ mục và vẫn còn nhiều mục nhập trong B có cùng id.

+0

'TÙY CHỌN CHỌN id TỪ LẠI THAM GIA B ON A.id = B.id Ở đâu B.id LÀ NULL' bạn có thể đăng kết quả giải thích cho cả hai truy vấn? – Igor

+0

Các chỉ mục có giúp ích gì không? – Londeren

+0

Đang chọn nếu 'COUNT (*) = 0' nhanh hơn? –

Trả lời

8
SELECT id FROM A LEFT OUTER JOIN B ON A.id=B.id WHERE B.id IS NULL 

bạn có thể thực hiện việc này. kết nối bên ngoài sẽ mang lại hiệu suất nhỏ nhưng không nhiều.

hệ thống cơ sở dữ liệu mới có thể sẽ tối ưu hóa truy vấn của bạn để không có bất kỳ sự khác biệt nào.

cách chính xác ở đây là lưu vào bộ nhớ cache! thử truy vấn cacher và ứng dụng cấp bộ nhớ đệm nếu có thể.

tất nhiên bạn cần chỉ mục thích hợp.

và thích hợp tôi có nghĩa là trên cả hai bảng và tốt nhất là một chỉ số băm vì nó sẽ có thời gian tra cứu tĩnh trong so sánh với bất kỳ cây có logarit

Hãy thử đặt một giải thích trước khi truy vấn để xem những gì thực sự chậm này .

nếu bạn thực sự cần điều này để được nhanh chóng, bạn có thể tái cấu trúc dữ liệu của bạn.

bạn có thể có thể tạo trình kích hoạt để đánh dấu cờ trong bảng A cho dù có mục nhập tương ứng trong bảng hay không. tất nhiên điều này dự phòng dữ liệu id, nhưng đôi khi nó có giá trị nó. chỉ nghĩ về nó như bộ nhớ đệm.

một ý nghĩ cuối cùng: bạn có thể thử SELECT id FROM A WHERE id NOT IN (SELECT id FROM B) nó có thể nhanh hơn một chút vì không cần tham gia thực tế, tuy nhiên nó cũng có thể chậm hơn vì tra cứu trong tập hợp sẽ là quét toàn bộ. Tôi không thực sự chắc chắn làm thế nào điều này sẽ được xử lý nhưng nó có thể có giá trị một thử.

+0

Đây là giải pháp tốt nhất ... Nó hoặc là phù hợp hoặc nó không, nhưng chỉ trả lại hồ sơ khi nó không tồn tại ... Chu kỳ đơn thông qua bảng cha ... Tương tự như cách tiếp cận tôi cũng đã cung cấp trong quá khứ. – DRapp

+2

Chỉ có MySQL có điều này: các công cụ khác tốt hơn với NOT EXISTS http://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-left-join-is-null-mysql/ – gbn

+0

Tôi nghĩ điểm quan trọng nhất bạn thực hiện là về các chỉ số băm. Tôi sẽ sử dụng chúng nếu có thể, nhưng InnoDB không hỗ trợ chúng, và tôi không chuẩn bị để chuyển đổi động cơ chỉ để làm cho truy vấn này hoạt động. – murgatroid99

1

Nó sẽ chậm bất kể bạn nhìn vào nó như thế nào. Hiệu suất trường hợp xấu nhất sẽ là một sự kết hợp chéo đầy đủ tạo ra 2 nghìn tỷ trận đấu tiềm năng (4 mill * 500k).

Cách thứ hai rất có thể sẽ hoạt động nhanh hơn vì đó là một truy vấn đơn lẻ.

1

Bạn có thể thử

SELECT id FROM A WHERE A.id NOT IN (SELECT id FROM B) 

nhưng tôi không biết nếu điều này sẽ được bất kỳ nhanh hơn. Tôi đã cố gắng tham gia bên trái đầu tiên. Tôi nghĩ rằng vấn đề của bạn là nhiều hơn để làm với chỉ mục. Bạn có chỉ mục trên cả hai trường id.

0

Đảm bảo có chỉ mục trên A.id và một chỉ mục khác trên B.id.

Điều gì có vẻ hơi kỳ lạ là bạn tham gia A.id với B.id. Là B.id chìa khóa nước ngoài để A hoặc nó là chìa khóa chính của B?

+0

B.id là khóa ngoài A và một nửa của khóa chính hai cột. – murgatroid99

+0

có quan trọng không? tất nhiên, cấu trúc dữ liệu có thể bị bẻ khóa .. –

+0

Chỉ muốn đảm bảo kết nối là ok. – phlogratos

1

Lập chỉ mục của bạn kém.

Đối với tất cả các hình thức (EXISTS, IN, LEFT JOIN) bạn nên có chỉ số trên id trong cả bảng

+0

id-s trông giống như PK nên truy vấn phải nhanh. – Igor

+0

@Igor: Hoặc bảng con có đại diện riêng của nó (không được sử dụng ở đây, id là cột FK) hoặc id là một phần của khóa tổng hợp. Trừ khi đó là một mối quan hệ 1: 1 ... Vì vậy, bạn không thể giả định các chỉ số chính xác trên cả hai bên – gbn

+0

B.id chắc chắn không có PK vì có nhiều hàng với cùng một id trong B. – phlogratos

0

Nếu schema của bạn là một cái gì đó như thế này:

CREATE TABLE b(
    id int, 
    value varchar(255) 
) 

CREATE TABLE a(
    id int, 
    father_id int, 
    value varchar(255) 
) 

Nếu bạn muốn tất cả các các hàng của bảng A không có con trong bảng A tại sao bạn không thử một cái gì đó như:

SELECT * FROM B WHERE id NOT IN (SELECT father_id FROM A GROUP BY father_id) 

Tôi chưa thử nhưng tôi nghĩ rằng nó 's fester. Hãy nhớ đặt chỉ mục qua id

Hy vọng điều này sẽ giúp

0

Tại sao không thử giá trị trống thay vì NULL. Trong SQL, giá trị NULL không bao giờ đúng so với bất kỳ giá trị nào khác, ngay cả NULL. Một biểu thức có chứa NULL luôn luôn tạo ra một giá trị NULL trừ khi có chỉ định khác trong tài liệu cho các toán tử và các hàm liên quan đến biểu thức.

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