SQL Select mengambil terlalu banyak waktu untuk dieksekusi

9

Ini adalah pemilihan sederhana dari tabel sementara, kiri bergabung dengan tabel yang ada pada kunci utama, dengan dua sub memilih menggunakan top 1 merujuk tabel bergabung.

Dalam kode:

SELECT
    TempTable.Col1,
    TempTable.Col2,
    TempTable.Col3,
    JoinedTable.Col1,
    JoinedTable.Col2,
    (
        SELECT TOP 1
            ThirdTable.Col1 -- Which is ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn1,
    (
        SELECT TOP 1
            ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn2,
FROM
    #TempTable as TempTable
LEFT JOIN
    JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn1 AND 
    TempTable.PKColumn2 = JoinedTable.PKColumn2)
WHERE
    JoinedTable.WhereColumn IN  (1, 3)

Ini adalah replika persis dari kueri saya.

Jika saya menghapus dua sub seleksi, itu berjalan dengan baik dan cepat. Dengan dua sub pilihan, saya mendapatkan sekitar 100 catatan per detik, yang sangat lambat untuk kueri ini karena harus mengembalikan hampir satu juta catatan.

Saya telah memeriksa untuk melihat apakah setiap tabel memiliki Kunci Utama, semuanya memiliki. Mereka semua memiliki Indeks DAN statistik untuk kolom penting mereka, seperti yang ada di klausa WHERE itu, dan yang ada di klausa GABUNG. Satu-satunya tabel tanpa kunci primer yang ditentukan atau indeks adalah tabel sementara, tetapi itu bukan masalah juga karena itu bukan yang terkait dengan subseleksi yang lambat, dan seperti yang saya sebutkan, tanpa sub seleksi itu berjalan dengan baik.

Tanpa yang TOP 1mengembalikan lebih dari satu hasil, dan menimbulkan kesalahan.

Tolong, tolong siapa?

EDIT :

Jadi rencana eksekusi memberitahuku bahwa aku kehilangan Indeks. Saya telah membuatnya, dan menciptakan kembali beberapa indeks lainnya. Setelah beberapa saat, rencana eksekusi menggunakannya, dan permintaan sekarang berjalan cepat. Satu-satunya masalah adalah saya tidak berhasil melakukan ini lagi di server lain, untuk permintaan yang sama. Jadi solusi saya akan ke PETUNJUK indeks SQL Server yang akan digunakan.

Smur
sumber
Wow, itu mengesankan. Tetapi bisakah Anda membagi ini menjadi beberapa pernyataan terpisah? Juga, bagaimana dengan Prosedur yang Disimpan sebagai gantinya?
2
@Adel Yang dipilih sebenarnya adalah sub pilih di dalam Prosedur Tersimpan. Semuanya sebenarnya cukup besar, tetapi saya 100% yakin bahwa itu adalah bagian yang tepat yang membutuhkan waktu untuk dieksekusi.
Perubahan dalam rencana pelaksanaan, termasuk indeks yang dipilih secara otomatis, kemungkinan besar berkaitan dengan perubahan data. Saya akan memastikan indeks Anda sepenuhnya menutupi atau mesin akan mengambil jalur yang tidak terduga seperti pemindaian tabel. Saya sarankan meninjau rencana eksekusi pada server baru (tanpa petunjuk) untuk melihat di mana Anda mengalami penyimpangan dari sistem asli.
Robert Miller
Saya melihat. Saya hanya mengubah server, basis datanya sama, dengan Indeks yang sama. Namun, sepertinya tidak secara otomatis memilih untuk menggunakan Indeks saya. Itu tidak persis apa yang Anda katakan: Pemindaian Tabel.
Smur
Kedengarannya seperti opitmizer kueri tidak suka indeks tabel untuk kueri Anda. Apakah rencana eksekusi menunjukkan indeks yang hilang?
Robert Miller

Jawaban:

7

Saya pikir dalam sejuta catatan kueri, Anda harus menghindari hal-hal seperti OUTER JOINS. Saya sarankan Anda menggunakan UNION ALLAlih-alih LEFT JOIN. Selama saya pikir CROSS APPLYlebih efisien daripada sub-kueri dalam klausa pilih, saya akan memodifikasi kueri yang ditulis oleh Conard Frix, yang menurut saya benar.

sekarang: ketika saya mulai memodifikasi query Anda saya melihat bahwa Anda memiliki klausa WHERE mengatakan: JoinedTable.WhereColumn IN (1, 3). dalam hal ini, jika bidangnya nol maka kondisinya akan menjadi salah. lalu mengapa Anda menggunakan LEFT JOIN saat Anda memfilter baris bernilai nol? cukup ganti LEFT JOINDengan INNER JOIN, saya jamin itu akan menjadi lebih cepat.

tentang INDEX:

harap dicatat bahwa ketika Anda memiliki indeks di atas meja, katakanlah

table1(a int, b nvarchar)

dan indeks Anda adalah:

nonclustered index ix1 on table1(a)

dan Anda ingin melakukan sesuatu seperti ini:

select a,b from table1
where a < 10

dalam indeks Anda, Anda belum memasukkan kolom bjadi apa yang terjadi?

jika sql-server menggunakan indeks Anda, itu harus mencari dalam indeks, yang disebut "Indeks Mencari" dan kemudian merujuk ke tabel utama untuk mendapatkan kolom b, yang disebut "Mencari" . Prosedur ini mungkin memakan waktu lebih lama daripada memindai tabel itu sendiri: "Pemindaian Tabel" .

tetapi berdasarkan statistik yang dimiliki sql-server, dalam situasi seperti itu, mungkin tidak menggunakan indeks Anda sama sekali.

jadi pertama-tama periksa Execution Planuntuk melihat apakah indeks digunakan sama sekali.

jika ya atau tidak keduanya, ubah indeks Anda untuk memasukkan semua kolom yang Anda pilih. katakan seperti:

nonclustered index ix1 on table1(a) include(b)

dalam hal ini Look Up tidak diperlukan, dan permintaan Anda akan dieksekusi jauh lebih cepat.


sumber
1
Saya tidak dapat mengubah yang tersisa bergabung menjadi Bergabung dalam, itu akan mengacaukan hasilnya, ini adalah aturan bisnis: tabel kedua tidak harus memiliki catatan terkait. Juga, kolom dalam klausa WHERE tidak menerima nilai nol.
Smur
6

Subnya memilih dalam pemilihan kolom Anda yang menyebabkan pengembalian lambat. Anda harus mencoba menggunakan sub-pilihan Anda di gabungan kiri, atau menggunakan tabel turunan seperti yang telah saya tentukan di bawah.

Menggunakan Left Joins untuk dua contoh dari Third Table

SELECT
  TempTable.Col1,
  TempTable.Col2,
  TempTable.Col3,
  JoinedTable.Col1,
  JoinedTable.Col2,
  ThirdTable.Col1 AS ThirdTableColumn1,
  ThirdTable2.Col1 AS ThirdTableColumn2
FROM #TempTable as TempTable
LEFT JOIN JoinedTable ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND 
    TempTable.PKColumn 2 = JoinedTable.PKColumn2)
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
WHERE
    JoinedTable.WhereColumn IN  (1, 3)

Menggunakan Tabel Turunan

 SELECT 
      TempTable.Col1,
      TempTable.Col2,
      TempTable.Col3,
      DerivedTable.Col1,
      DerivedTable.Col2,
      DerivedTable.ThirdTableColumn1,
      DerivedTable.ThirdTableColumn2
 FROM #TempTable as TempTable
    LEFT JOIN (SELECT
                 JoinedTable.PKColumn2,
                 JoinedTable.Col1,
                 JoinedTable.Col2,
                 JoinedTable.WhereColumn,
                 ThirdTable.Col1 AS ThirdTableColumn1,
                 ThirdTable2.Col1 AS ThirdTableColumn2
               FROM JoinedTable
               LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
               LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn) 
        DerivedTable ON (TempTable.PKColumn1 = DerivedTable .PKColumn2 AND 
        TempTable.PKColumn2 = DerivedTable.PKColumn2)
    WHERE
        DerivedTable.WhereColumn IN  (1, 3)
John Hartsock
sumber
2

Coba beri tanda silang

SELECT
    TempTable.Col1,
    TempTable.Col2,
    TempTable.Col3,
    JoinedTable.Col1,
    JoinedTable.Col2,
    ThirdTableColumn1.col1,
    ThirdTableColumn2.col1

FROM
    #TempTable as TempTable
LEFT JOIN
    JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND 
    TempTable.PKColumn 2 = JoinedTablePKColumn2)

CROSS APPLY
(
        SELECT TOP 1
            ThirdTable.Col1 -- Which is ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn1
CROSS APPLY    (
        SELECT TOP 1
            ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn2,
WHERE
    JoinedTable.WhereColumn IN  (1, 3)

Anda juga dapat menggunakan CTE dan row_number atau kueri inline menggunakan MIN

Conrad Frix
sumber
2

Pindahkan bit JOIN dari bagian utama klausa dan letakkan sebagai subselect. Memindahkannya ke bagian WHERE dan JOIN menjamin Anda tidak perlu SELECT TOP 1 berulang-ulang, yang saya percaya adalah alasan kelambatan hte. Jika Anda ingin memeriksa ini, periksa rencana eksekusi.


sumber
2

Itu ThirdTable referensi, (sub menyeleksi dalam contoh Anda), perlu indeks perhatian yang sama seperti bagian lain dari query.

Terlepas dari apakah Anda menggunakan sub pilih:

(
    SELECT TOP 1
        ThirdTable.Col1 -- Which is ThirdTable's Primary Key
    FROM
        ThirdTable
    WHERE
        ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1,
(
    SELECT TOP 1
        ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
    FROM
        ThirdTable
    WHERE
        ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,

LEFT BERGABUNG (seperti yang diusulkan oleh John Hartsock):

LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn

LINTAS BERLAKU (seperti yang diusulkan oleh Conrad Frix):

CROSS APPLY
(
        SELECT TOP 1
            ThirdTable.Col1 -- Which is ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn1
CROSS APPLY    (
        SELECT TOP 1
            ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
        FROM
            ThirdTable
        WHERE
            ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
    ) as ThirdTableColumn2

Anda perlu memastikan covering indexesuntuk didefinisikan ThirdTable.SomeColumndan ThirdTable.SomeOtherColumndan indeks unik. Ini berarti Anda perlu lebih lanjut memenuhi syarat ThirdTablereferensi untuk menghilangkan pemilihan beberapa baris dan meningkatkan kinerja. Pilihan sub selects, LEFT JOINatau CROSS APPLYtidak akan benar-benar peduli sampai Anda meningkatkan selektivitas untuk ThirdTable.SomeColumndanThirdTable.SomeOtherColumn oleh termasuk lebih kolom untuk memastikan selektivitas yang unik. Sampai saat itu, saya berharap kinerja Anda akan terus menderita.

Itu covering index topik yang baik diperkenalkan oleh Maziar Taheri; sementara tidak mengulangi pekerjaannya, saya menekankan perlunya untuk mengambil hati menggunakan indeks yang meliputi.

Singkatnya: Tingkatkan selektivitas untuk ThirdTable.SomeColumndan ThirdTable.SomeOtherColumnkueri (atau gabung ) dengan menambahkan kolom dalam-tabel terkait untuk memastikan kecocokan baris yang unik. Jika ini tidak memungkinkan, maka Anda akan terus mengalami masalah kinerja karena mesin sibuk menarik baris yang kemudian dibuang. Ini berdampak pada i / o, cpu, dan, akhirnya, rencana eksekusi Anda.

Robert Miller
sumber