Sorgu Nasıl Optimize Edilir


9

Buna benzer bir veritabanı yapım var,

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

DispatchLink tablosunun amacı iki Dispatch kaydını birbirine bağlamaktır. Bu arada, miras nedeniyle gönderim masamda bileşik bir birincil anahtar kullanıyorum, bu yüzden çok fazla acı olmadan değiştiremiyorum. Ayrıca bağlantı tablosu bunu yapmanın doğru yolu olmayabilir mi? Ama yine miras.

Benim sorum, eğer bu sorguyu çalıştırırsam

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

DispatchLink tablosunda bir dizin araması yapmak için asla alamadım. Her zaman tam bir dizin taraması yapar. Bu, birkaç kayıtla sorun değil, ancak bu tabloda 50000 olduğunda, sorgu planına göre dizinde 50000 kaydı tarar. Birleştirme yan tümcesinde 'ands' ve 'ors' olduğu için, ancak SQL'in neden birkaç dizin araması yapamadığını, 'veya' sol tarafında bir tane, biri de 'veya' nın sağ tarafı için.

Ben bunun için bir açıklama istiyorum, sorgu ayarlama olmadan yapılabilir daha hızlı sorgu yapmak için bir öneri istiyorum. Bunun nedeni, birleştirme çoğaltma birleştirme filtresi olarak yukarıdaki sorguyu kullanıyorum, bu yüzden sadece ne yazık ki sorgu başka bir tür ekleyemezsiniz.

GÜNCELLEME: Örneğin, bunlar benim eklediğim dizin türleri,

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)

Dizinleri kullanır, ancak tüm dizin boyunca bir dizin taraması yapar, böylece 50000 kayıtlar dizinde 50000 kayıtları tarar.


Sorgunuzda: "Dispatch d inner'den * seçin * d.DispatchId = dl.DispatchLink1 ve d.ContractId = dl.ContractLink1 veya d.DispatchId = dl.DispatchLink2 ve d.ContractId = dl.ContractLink2 ve d.ContractId = dl.ContractLink2" "OR" koşulu ile değiştirin ve her biri "OR" kullanarak 2 SELECT ifadesinin BİRLİĞİ ile değiştirin, ayrıca testi mümkün olduğunca saf hale getirmek için "*" yerine her iki SELECT öğesindeki tek anahtar sütunları kullanın.
NoChance

Teşekkürler SQL Kiwi, bu daha önce denediğim bir şey ama maalesef işe yaramadı.
peter

Emmad Kareem, iç birleşim kullanmamanızı mı öneriyorsunuz? Seni gerçekten takip etmiyorum üzgünüm.
peter

İç birleştirme mantığı iyidir, ancak "OR" operatörünü önlemek ve dolayısıyla indeksleri kullanma olasılığını artırmak için eski stil WHERE koşulu kullanılarak yazılabilir. Buradaki "OR koşullarıyla sorguyu UNION olarak yeniden yazma" bölümüne bakın: componentace.com/help/absdb_manual/increasesqlperformance.htm
NoChance 6:02

1
Çoğaltma sorunu daha basit bir sorgunuz olabilir mi: Dispatch d iç birleşiminden * seçin * d.DispatchId = dl.DispatchLink1 ve d.ContractId = dl.ContractLink1 'de evet ise, sonuçların hala geçerli olması için DispatchLink'teki verileri çoğaltabiliriz ...
AK

Yanıtlar:


12

Optimize edici, birçok plan alternatifini (birden çok arayışa sahip olanlar dahil) dikkate alabilir, ancak disjeksiyonlar ( ORtahminler) için varsayılan olarak dizin kesişimlerini içeren planları dikkate almaz. Endeksler verildiğinde:

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

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

Dizin aramalarını zorlayabiliriz (SQL Server 2008 veya üstünü varsayarak):

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);

FORCESEEK planı

Örnek verilerinizi kullanarak, arama planı tarama planı için 0,0068057 ile karşılaştırıldığında 0,0332551 birimdir :

Tarama planı

Deneyebileceğimiz her türlü sorgu yeniden yazma ve ipucu vardır. Optimize edicinin orijinal plan için dikkate almadığı bir seçeneği tanıtmak için yeniden yazma örneğidir:

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;

Bu yürütme planı, ilkinde bir eşleşme bulursa ikinci dizini aramaz:

TOP Planını Uygula

Bu, varsayılan FORCESEEKplandan çok daha iyi performans gösterebilir .

Yeni dizinler eklemeden, Gönderme tablosuna da bir arama zorlayabiliriz:

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);

Ara 2

Tabloların her birinde kaç satır olduğu gibi şeylere bağlı olarak bu ilk örnekten daha iyi veya daha kötü olabilir. APPLY + TOPİyileştirme hala mümkündür:

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;

Bu çok faydalı bir cevap. Başka bir soru sordum dba.stackexchange.com/questions/23773/analysing-a-query-plan gerçek veri üzerinde gerçek sorgu planını gösterir (test verilerim değil). Sorgu planındaki darboğazın tam olarak ne olduğunu anlayabilecek bilgiye sahip değilim. Belki bir göz atabilirsin?
peter

Bu gerçekten ilginç çünkü 'FORCESEEK' eklemek, sorgumu 10 dakikadan fazla almak yerine 9 saniye içinde çalıştırıyor. Güncelleme istatistikleri fark etmez. Sorgu analizörü neden bu kadar yanlış yapıyor?
peter

Bence tasarım konusunda haklısın. Sütunları tekrarlamakla ne demek istiyorsun? İlişkili olarak iki Dağıtım kaydını birbirine bağlaması gereken bir tablo yapısını nasıl tasarlarsınız? 'Gerçek' tablo kendi birincil anahtar alanına sahip olsa da açıklığa kavuşturmak için evet, ancak Dispatch'de bileşik bir anahtarın olması tam olarak yardımcı olmuyor.
peter

SQL Kivi. Yinelenen sütunlar. Anladım, teşekkürler.
peter
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.