Cara Mengoptimalkan Permintaan

9

Saya memiliki struktur database yang mirip dengan ini,

CREATE TABLE [dbo].[Dispatch](
    [DispatchId] [int] NOT NULL,
    [ContractId] [int] NOT NULL,
    [DispatchDescription] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Dispatch] PRIMARY KEY CLUSTERED 
(
    [DispatchId] ASC,
    [ContractId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

CREATE TABLE [dbo].[DispatchLink](
    [ContractLink1] [int] NOT NULL,
    [DispatchLink1] [int] NOT NULL,
    [ContractLink2] [int] NOT NULL,
    [DispatchLink2] [int] NOT NULL
) ON [PRIMARY]

GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (1, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (2, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (3, 1, N'Test')
GO
INSERT [dbo].[Dispatch] ([DispatchId], [ContractId], [DispatchDescription]) VALUES (4, 1, N'Test')
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 1, 1, 2)
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 1, 1, 3)
GO
INSERT [dbo].[DispatchLink] ([ContractLink1], [DispatchLink1], [ContractLink2], [DispatchLink2]) VALUES (1, 3, 1, 2)
GO

Inti dari tabel DispatchLink adalah untuk menghubungkan dua catatan pengiriman bersama. Ngomong-ngomong saya menggunakan kunci primer komposit di meja pengiriman saya karena peninggalan, jadi saya tidak bisa mengubahnya tanpa banyak rasa sakit. Juga tabel tautan mungkin bukan cara yang benar untuk melakukannya? Tapi sekali lagi warisan.

Jadi pertanyaan saya, jika saya menjalankan kueri ini

select * from Dispatch d
inner join DispatchLink dl on d.DispatchId = dl.DispatchLink1 and d.ContractId = dl.ContractLink1
or d.DispatchId = dl.DispatchLink2 and d.ContractId = dl.ContractLink2

Saya tidak pernah bisa mendapatkannya untuk melakukan pencarian indeks di tabel DispatchLink. Itu selalu melakukan pemindaian indeks penuh. Itu bagus dengan beberapa catatan, tetapi ketika Anda memiliki 50.000 di tabel itu, memindai 50000 catatan dalam indeks sesuai dengan rencana kueri. Itu karena ada 'ands' dan 'ors' di klausa join, tapi saya tidak bisa mengerti mengapa SQL tidak dapat melakukan beberapa pencarian indeks, satu untuk sisi kiri dari 'atau', dan satu untuk sisi kanan 'atau'.

Saya ingin penjelasan untuk ini, bukan saran untuk membuat kueri lebih cepat kecuali itu bisa dilakukan tanpa menyesuaikan kueri. Alasannya adalah bahwa saya menggunakan kueri di atas sebagai gabungan filter gabung replikasi, jadi saya tidak bisa menambahkan jenis permintaan lain sayangnya.

UPDATE: Misalnya ini adalah jenis indeks yang telah saya tambahkan,

CREATE NONCLUSTERED INDEX IDX1 ON DispatchLink (ContractLink1, DispatchLink1)
CREATE NONCLUSTERED INDEX IDX2 ON DispatchLink (ContractLink2, DispatchLink2)
CREATE NONCLUSTERED INDEX IDX3 ON DispatchLink (ContractLink1, DispatchLink1, ContractLink2, DispatchLink2)

Jadi ia menggunakan indeks, tetapi melakukan pemindaian indeks di seluruh indeks, jadi 50000 catatan itu memindai 50.000 catatan dalam indeks.

Peter
sumber
Apakah Anda memiliki indeks di atas DispatchLinkmeja?
ypercubeᵀᴹ
Saya telah menambahkan indeks yang telah saya coba di atas.
peter
Dalam kueri Anda: "pilih * dari Dispatch d bagian dalam DispatchLink dl di d.DispatchId = dl.DispatchLink1 dan d.ContractId = dl.ContractLink1 atau d.DispatchId = dl.DispatchLink2 dan d.ContractId = dl.ContractLink2" coba hapus kondisi "ATAU" dan menggantinya dengan UNION 2 pernyataan SELECT masing-masing tidak menggunakan "ATAU", juga menggunakan satu-satunya kolom kunci di kedua SELECT bukannya "*", hanya untuk membuat tes semurni mungkin.
NoChance
Terima kasih SQL Kiwi, ini adalah sesuatu yang saya coba sebelumnya tetapi sayangnya tidak berhasil.
peter
1
Bisakah Anda memiliki masalah replikasi kueri yang lebih sederhana: pilih * dari Dispatch dan gabung dalam DispatchLink dl di d.DispatchId = dl.DispatchLink1 dan d.ContractId = dl.ContractLink1 Jika ya, kami dapat menduplikasi data di DispatchLink sehingga hasilnya masih valid ...
AK

Jawaban:

12

Pengoptimal dapat mempertimbangkan banyak alternatif rencana (termasuk yang dengan banyak pencarian) tetapi untuk disjungsi ( ORpredikat), pengoptimal tidak mempertimbangkan rencana yang melibatkan persimpangan indeks secara default. Diberikan indeks:

CREATE CLUSTERED INDEX cx 
ON dbo.DispatchLink (DispatchLink1, ContractLink1);

CREATE NONCLUSTERED INDEX nc1 
ON dbo.DispatchLink (DispatchLink2, ContractLink2);

Kami dapat memaksa pencarian indeks (dengan asumsi SQL Server 2008 atau yang lebih baru):

SELECT * 
FROM dbo.Dispatch AS d
INNER JOIN dbo.DispatchLink AS dl WITH (FORCESEEK) ON 
    (d.DispatchId = dl.DispatchLink1 AND d.ContractId = dl.ContractLink1)
    OR (d.DispatchId = dl.DispatchLink2 AND d.ContractId = dl.ContractLink2);

Rencana FORCESEEK

Menggunakan data sampel Anda, biaya pencarian paket di 0,0332551 unit dibandingkan dengan 0,0068057 untuk rencana pemindaian:

Rencana pemindaian

Ada segala macam kemungkinan penulisan ulang dan petunjuk yang bisa kita coba. Salah satu contoh penulisan ulang untuk mempromosikan opsi yang tidak dipertimbangkan oleh optimizer untuk paket aslinya adalah:

SELECT * 
FROM dbo.Dispatch AS d
CROSS APPLY
(
    SELECT TOP (1) * FROM
    (
        SELECT * FROM dbo.DispatchLink AS dl
        WHERE dl.DispatchLink1 = d.DispatchId
        AND dl.ContractLink1 = d.ContractId
        UNION ALL
        SELECT * FROM dbo.DispatchLink AS dl
        WHERE dl.DispatchLink2 = d.DispatchId
        AND dl.ContractLink2 = d.ContractId
    ) SQ1
) AS F1;

Rencana eksekusi ini tidak mencari indeks kedua jika menemukan kecocokan pada yang pertama:

BERLAKU TOP Plan

Ini mungkin berkinerja sangat sedikit lebih baik daripada paket default FORCESEEK.

Tanpa menambahkan indeks baru, kami juga dapat memaksa pencarian ke dalam tabel Pengiriman:

SELECT * 
FROM dbo.DispatchLink AS dl
JOIN dbo.Dispatch AS d WITH (FORCESEEK) ON
    (d.DispatchId = dl.DispatchLink1 AND d.ContractId = dl.ContractLink1)
    OR (d.DispatchId = dl.DispatchLink2 AND d.ContractId = dl.ContractLink2);

Carilah 2

Ini mungkin lebih baik atau lebih buruk daripada contoh pertama tergantung pada hal-hal seperti berapa banyak baris di setiap tabel. The APPLY + TOPperbaikan masih mungkin:

SELECT * 
FROM dbo.DispatchLink AS dl
CROSS APPLY
(
    SELECT TOP (1) * FROM
    (
        SELECT * FROM dbo.Dispatch AS d
        WHERE dl.DispatchLink1 = d.DispatchId
        AND dl.ContractLink1 = d.ContractId
        UNION ALL
        SELECT * FROM dbo.Dispatch AS d
        WHERE dl.DispatchLink2 = d.DispatchId
        AND dl.ContractLink2 = d.ContractId
    ) SQ1
) AS F1;
Paul White 9
sumber
Itu jawaban yang sangat berguna. Saya telah mengajukan pertanyaan lain dba.stackexchange.com/questions/23773/analysing-a-query-plan yang menunjukkan rencana permintaan aktual pada data nyata (bukan data pengujian saya). Saya tidak memiliki pengetahuan untuk memahami apa yang menjadi hambatan pada rencana kueri. Mungkin Anda bisa melihatnya?
peter
Ini sangat menarik karena menambahkan 'FORCESEEK' membuat kueri saya berjalan dalam 9 detik daripada mengambil lebih dari 10 menit. Perbarui statistik tidak ada bedanya. Mengapa lagi penganalisis permintaan salah?
peter
Saya pikir Anda benar tentang desain. Apa maksud Anda tentang pengulangan kolom? Bagaimana Anda mendesain struktur tabel yang harus menghubungkan dua catatan Pengiriman bersama-sama sebagai terkait? Untuk memperjelas meskipun tabel 'nyata' memang memiliki bidang kunci utama sendiri, tapi ya memiliki kunci komposit di Dispatch tidak membantu.
peter
SQL Kiwi. Mengulangi kolom. Terima kasih, terima kasih.
peter