Subquery MySQL melambat drastis, tetapi mereka bekerja dengan baik secara mandiri

8

Pertanyaan 1:

select distinct email from mybigtable where account_id=345

membutuhkan 0,1s

Pertanyaan 2:

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

membutuhkan 0,2s

Pertanyaan 3:

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

membutuhkan 22 menit dan 90% dalam keadaan "mempersiapkan". Mengapa ini membutuhkan banyak waktu.

Tabel adalah innodb dengan baris 3.2mil di MySQL 5.0

Stewie
sumber

Jawaban:

8

Di Query 3, Anda pada dasarnya menjalankan subquery untuk setiap baris mybigtable melawan dirinya sendiri.

Untuk menghindari ini, Anda perlu membuat dua perubahan besar:

PERUBAHAN UTAMA # 1: Refactor the Query

Ini pertanyaan asli Anda

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

Kamu bisa mencoba

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;

atau mungkin hitungan per 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;

PERUBAHAN UTAMA # 2: Pengindeksan yang Tepat

Saya pikir Anda sudah memiliki ini sejak Kueri 1 dan Kueri 2 berjalan cepat. Pastikan Anda memiliki indeks gabungan pada (account_id, email). Lakukan SHOW CREATE TABLE mybigtable\Gdan pastikan Anda memilikinya. Jika Anda tidak memilikinya atau jika Anda tidak yakin, tetap buat indeks:

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

UPDATE 2012-03-07 13:26 EST

Jika Anda ingin melakukan NOT IN (), ubah INNER JOINto a LEFT JOINdan periksa sisi kanan NULL, seperti ini:

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;

UPDATE 2012-03-07 14:13 EST

Silakan baca dua tautan ini untuk melakukan GABUNG

Ini adalah Video YouTube yang luar biasa tempat saya belajar untuk memperbaiki pertanyaan dan buku yang menjadi dasarnya

RolandoMySQLDBA
sumber
9

Di MySQL, subselek dalam klausa IN dieksekusi ulang untuk setiap baris dalam kueri luar, sehingga menciptakan O (n ^ 2). Ceritanya adalah, jangan gunakan IN (SELECT).

Aaron Brown
sumber
1
  1. Apakah Anda memiliki indeks di account_id?

  2. Masalah kedua mungkin dengan sub-kueri bersarang yang memiliki kinerja mengerikan di 5.0.

  3. KELOMPOK OLEH dengan klausa memiliki lebih cepat daripada DISTINCT.

  4. Apa yang Anda coba lakukan yang mungkin lebih baik dilakukan melalui gabungan selain Item # 3?

Stephen Senkomago Musoke
sumber
1

Ada banyak pemrosesan yang terlibat saat menangani subquery IN () seperti milik Anda. Anda dapat membaca lebih lanjut di sini .

Saran pertama saya adalah mencoba menulis ulang subquery menjadi GABUNG. Sesuatu seperti (tidak diuji):

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
Derek Downey
sumber