2011-10-25 33 views
10

Tôi đã có truy vấn chứa truy vấn phụ luôn gây ra QUÉT của một bảng rất lớn dẫn đến thời gian truy vấn kém.Tại sao truy vấn con gây ra quá trình quét khi danh sách tĩnh không?

Đây là câu hỏi tôi đang sử dụng:

SELECT PersonId 
    FROM person 
WHERE PersonId IN (
        SELECT PersonId 
         FROM relationship 
        WHERE RelatedToPersonId = 12270351721 
        ); 

Kế hoạch truy vấn được báo cáo là:

SCAN TABLE person (~100000 rows) 
EXECUTE LIST SUBQUERY 1 
SEARCH TABLE relationship USING INDEX relationship_RelatedToPersonId_IDX (RelatedToPersonId=?) (~10 rows) 

Các truy vấn cùng với một danh sách tĩnh (tương đương với kết quả của subquery) :

SELECT PersonId 
    FROM person 
WHERE PersonId IN (12270351727,12270351730,12270367969,12387741400); 

Và kế hoạch truy vấn cho điều đó:

SEARCH TABLE person USING COVERING INDEX sqlite_autoindex_person_1 (PersonId=?) (~5 rows) 
EXECUTE LIST SUBQUERY 1 

Tại sao truy vấn đầu tiên sẽ nhắc quét nếu lần truy vấn thứ hai không?

+0

Nếu tập dữ liệu từ truy vấn phụ của bạn nhỏ thì bảng tạm thời có thể hoạt động. Một cái gì đó như: Chọn Personid From (SELECT PersonId FROM relationship WHERE RelatedToPersonId = 12270351721); – DallinDyer

+0

Tôi đã thử một bảng tạm thời và nó vẫn tạo ra một lần quét. – goto10

+0

Có thể 'PersonId' tồn tại trong' mối quan hệ' nếu không có hàng trong 'người' không? Nếu không, không phải truy vấn phụ (có thể với một 'khác biệt') thỏa mãn toàn bộ truy vấn? –

Trả lời

2

Bảng phải được quét vì bạn đang bao gồm một trường khác (RelatedToPersonId) trong mệnh đề WHERE của truy vấn phụ của bạn. Danh sách các PersonID có thể đi trực tiếp đến chỉ mục.

+0

RelatedToPersonId nằm trong bảng quan hệ. Tại sao điều đó lại gây ra việc quét bảng người? Truy vấn phụ đó có thực thi độc lập không, tạo danh sách kết quả của nó và sau đó hành xử giống như truy vấn với danh sách giá trị tĩnh? Nếu bạn nhìn vào kế hoạch truy vấn cho truy vấn đó, phần truy vấn phụ thực sự nhấn chỉ mục. – goto10

+0

Trình tối ưu hóa SQLite chắc chắn có thể biết rằng truy vấn phụ có thể sử dụng một chỉ mục và đảm bảo thực hiện nó trước và sau đó hạn chế các hàng từ bảng người đến tập kết quả đó. – jjxtra

0

này:

SELECT PersonId 
    FROM person 
WHERE PersonId IN (12270351727,12270351730,12270367969,12387741400); 

chỉ đơn thuần là đường cú pháp cho việc này:

SELECT PersonId 
    FROM person 
WHERE (
     PersonId = 12270351727 
     OR PersonId = 12270351730 
     OR PersonId = 12270367969 
     OR PersonId = 12387741400 
     ); 
+0

Có, nhưng điều đó không giải thích tại sao việc quét là cần thiết khi truy vấn con được sử dụng hoặc, quan trọng hơn, cách tránh quét đó. Có vẻ như trình tối ưu hóa truy vấn SQLite không tạo ra một kế hoạch truy vấn tối ưu trong trường hợp này. – goto10

1

tôi không thể tái tạo. Tôi thực hiện những điều khoản sau đây trên một phiên bản SQLite tươi 3.7.8 cài đặt:

create table person (id int not null primary key, name varchar(20)); 
insert into person values (1, 'a'); 
insert into person select id + 1, name || 'b' from person; 
insert into person select id + 2, name || 'c' from person; 
insert into person select id + 4, name || 'd' from person; 
insert into person select id + 8, name || 'e' from person; 
insert into person select id + 16, name || 'f' from person; 
insert into person select id + 32, name || 'g' from person; 
insert into person select id + 64, name || 'h' from person; 
insert into person select id + 128, name || 'i' from person; 
insert into person select id + 256, name || 'j' from person; 
insert into person select id + 512, name || 'k' from person; 
insert into person select id + 512, name || 'l' from person; 
insert into person select id + 1024, name || 'l' from person; 
insert into person select id + 2048, name || 'm' from person; 
insert into person select id + 4096, name || 'n' from person; 
insert into person select id + 8192, name || 'o' from person; 
insert into person select id + 16384, name || 'p' from person; 
insert into person select id + 32768, name || 'q' from person; 
insert into person select id + 65536, name || 'r' from person; 
select count(*) from person; 

create table relation (id int, related int); 
insert into relation select id, id + 1 from person; 
insert into relation select id, id + 2 from person; 
insert into relation select id, id + 3 from person; 
insert into relation select id, id + 4 from person; 
insert into relation select id, id + 5 from person; 
insert into relation select id, id + 6 from person; 
insert into relation select id, id + 7 from person; 
insert into relation select id, id + 8 from person; 
insert into relation select id, id + 9 from person; 
insert into relation select id, id + 10 from person; 
delete from relation where related not in (select id from person); 

create index relatedToPerson on relation(related); 
explain query plan select id from person 
    where id in (select id from relation where related = 2345); 

Các kết quả cho các tuyên bố kế hoạch truy vấn:

0|0|0|SEARCH TABLE person USING COVERING INDEX sqlite_autoindex_person_1 (id=?)(~25 rows) 
0|0|0|EXECUTE LIST SUBQUERY 1 
1|0|0|SEARCH TABLE relation USING INDEX relatedToPerson (related=?) (~10 rows) 

Tại sao nó không làm việc cho bạn? Những lý do tôi có thể nghĩ:

  • mối quan hệ bảng của bạn không chứa các cột PersonId (xin xác minh)
  • bạn đang sử dụng một phiên bản khác của SQLite
  • bạn có trở ngại khác, ví dụ chỉ số độc đáo .

Bạn có thể chạy tập lệnh ở trên và xác minh xem bạn có nhận được kết quả tương tự không?

+0

Tôi đã quá bận rộn trong tuần này để có được điều này và tôi sẽ ra khỏi thành phố vào ngày mai, vì vậy tôi có thể không có khả năng dành thêm thời gian cho việc này trong vài ngày nữa. Nhưng tôi không muốn lãng phí tiền thưởng vì vậy tôi sẽ trao nó cho bạn vì những nỗ lực mà bạn đã đưa vào. Nếu ví dụ của bạn thực sự không quét thì điều đó sẽ cho tôi một nơi tốt để bắt đầu từ khi cố gắng hiểu tại sao tôi không làm việc như vậy. – goto10

+0

Chúc may mắn. Vui lòng đăng kết quả của bạn tại đây. Tôi tò mò sự khác biệt là gì. – boes

+0

Các kiểu dữ liệu không khớp (văn bản và số nguyên) – DallinDyer

1

Tôi không chắc chắn tại sao lại có quét bảng. Nhưng không có nhu cầu sử dụng một subselect đây: Bạn cũng có thể sử dụng một tham gia:

SELECT person.PersonId 
    FROM person JOIN relationship ON person.PersonId = relationship.PersonId 
WHERE RelatedToPersonId = 12270351721; 

Không chắc chắn nếu sqlite hỗ trợ JOIN này, nhưng điều này cũng có thể được viết lại bằng FROM và ĐỊA ĐIỂM:

SELECT person.PersonId 
    FROM person, relationship 
WHERE person.PersonId = relationship.PersonId 
    AND RelatedToPersonId = 12270351721; 

Và, có thể đây là điều mà trình tối ưu hóa được điều chỉnh.

+0

Như tôi đã đề cập trong phần bình luận ở trên, đây là một truy vấn giả tạo thể hiện một trường hợp rất đơn giản của một lần quét bất ngờ. Truy vấn "cuộc sống thực" phức tạp hơn nhiều nhưng lại liên quan đến tình huống tương tự - truy vấn con tạo ra một danh sách ngắn tạo ra quá trình quét trong khi đặt cùng các giá trị trong danh sách tĩnh không gây ra quá trình quét. – goto10

2

Có thể là các kiểu dữ liệu chưa từng có. Person.PersonId và mối quan hệ.PersonId có định nghĩa dữ liệu giống nhau không.

+1

Bật. Cảm ơn! – DallinDyer

0

Nó quét toàn bộ vì truy vấn phụ được thực thi cho mỗi hàng trong person. Truy vấn phụ không "lưu" bộ kết quả của chúng được chuyển đổi thành IN.

Hy vọng điều đó sẽ hữu ích.

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