Truy vấn con MySQL chậm lại một cách mạnh mẽ, nhưng chúng hoạt động độc lập tốt


8

Truy vấn 1:

select distinct email from mybigtable where account_id=345

mất 0,1 giây

Truy vấn 2:

Select count(*) as total from mybigtable where account_id=123 and email IN (<include all from above result>)

mất 0,2 giây

Truy vấn 3:

Select count(*) as total from mybigtable where account_id=123 and email IN (select distinct email from mybigtable where account_id=345)

mất 22 phút và 90% trong trạng thái "chuẩn bị". Tại sao điều này mất quá nhiều thời gian.

Bảng là innodb với 3,2 triệu hàng trên MySQL 5.0

Câu trả lời:


8

Trong Truy vấn 3, về cơ bản, bạn đang thực hiện một truy vấn con cho mỗi hàng mybigtable đối với chính nó.

Để tránh điều này, bạn cần thực hiện hai thay đổi lớn:

THAY ĐỔI CHỦ YẾU # 1: Tái cấu trúc truy vấn

Đây là truy vấn ban đầu của bạn

Select count(*) as total from mybigtable
where account_id=123 and email IN
(select distinct email from mybigtable where account_id=345)

Bạn có thể thử

select count(*) EmailCount from
(
    select tbl123.email from
    (select email from mybigtable where account_id=123) tbl123
    INNER JOIN
    (select distinct email from mybigtable where account_id=345) tbl345
    using (email)
) A;

hoặc có thể là số lượng trên mỗi email

select email,count(*) EmailCount from
(
    select tbl123.email from
    (select email from mybigtable where account_id=123) tbl123
    INNER JOIN
    (select distinct email from mybigtable where account_id=345) tbl345
    using (email)
) A group by email;

THAY ĐỔI CHỦ YẾU # 2: Lập chỉ mục đúng

Tôi nghĩ rằng bạn đã có điều này vì Truy vấn 1 và Truy vấn 2 chạy nhanh. Hãy chắc chắn rằng bạn có một chỉ mục tổng hợp trên (account_id, email). Làm SHOW CREATE TABLE mybigtable\Gvà chắc chắn rằng bạn có một cái. Nếu bạn không có nó hoặc nếu bạn không chắc chắn, thì hãy tạo chỉ mục bằng mọi cách:

ALTER TABLE mybigtable ADD INDEX account_id_email_ndx (account_id,email);

CẬP NHẬT 2012/03/07 13:26 EST

Nếu bạn muốn thực hiện KHÔNG IN (), hãy thay đổi INNER JOINthành a LEFT JOINvà kiểm tra phía bên phải là NULL, như thế này:

select count(*) EmailCount from
(
    select tbl123.email from
    (select email from mybigtable where account_id=123) tbl123
    LEFT JOIN
    (select distinct email from mybigtable where account_id=345) tbl345
    using (email)
    WHERE tbl345.email IS NULL
) A;

CẬP NHẬT 2012/03/07 14:13 EST

Vui lòng đọc hai liên kết này khi thực hiện THAM GIA

Đây là một Video YouTube tuyệt vời nơi tôi học cách tái cấu trúc các truy vấn và cuốn sách được dựa trên


9

Trong MySQL, các lựa chọn con trong mệnh đề IN được thực thi lại cho mỗi hàng trong truy vấn bên ngoài, do đó tạo O (n ^ 2). Truyện ngắn là, không sử dụng IN (CHỌN).


1
  1. Bạn có chỉ mục trên account_id không?

  2. Vấn đề thứ hai có thể là với các truy vấn phụ lồng nhau có hiệu năng khủng trong 5.0.

  3. NHÓM THEO với mệnh đề có nhanh hơn DISTINCT.

  4. Bạn đang cố gắng làm gì để có thể được thực hiện tốt hơn thông qua các phép nối ngoài Mục số 3?


1

Có rất nhiều xử lý liên quan khi xử lý một truy vấn con IN () như của bạn. Bạn có thể đọc thêm về nó ở đây .

Đề nghị đầu tiên của tôi sẽ là cố gắng viết lại truy vấn con vào THAM GIA thay thế. Một cái gì đó như (không được thử nghiệm):

SELECT COUNT(*) AS total FROM mybigtable AS t1
 INNER JOIN 
   (SELECT DISTINCT email FROM mybigtable WHERE account_id=345) AS t2 
   ON t2.email=t1.email
WHERE account_id=123
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.