IN () kullanarak sorgu performansını iyileştirme


14

Aşağıdaki SQL sorgusu var:

SELECT
  Event.ID,
  Event.IATA,
  Device.Name,
  EventType.Description,
  Event.Data1,
  Event.Data2
  Event.PLCTimeStamp,
  Event.EventTypeID
FROM
  Event
INNER JOIN EventType ON EventType.ID = Event.EventTypeID
INNER JOIN Device ON Device.ID = Event.DeviceID
WHERE
  Event.EventTypeID IN (3, 30, 40, 41, 42, 46, 49, 50)
  AND Event.PLCTimeStamp BETWEEN '2011-01-28' AND '2011-01-29'
  AND Event.IATA LIKE '%0005836217%'
ORDER BY Event.ID;

Ayrıca Eventsütun için bir dizin var TimeStamp. Anladığım kadarıyla bu indeks IN()ifade nedeniyle kullanılmıyor . Benim sorum şu IN()sorguyu hızlandırmak için bu belirli ifade için bir dizin yapmak için bir yolu var mı?

Ayrıca Event.EventTypeID IN (2, 5, 7, 8, 9, 14)dizin için bir filtre olarak eklemeyi denedim TimeStamp, ancak yürütme planına bakarken bu dizini kullanıyor gibi görünmüyor. Bununla ilgili herhangi bir öneri veya öngörü çok takdir edilecektir.

Grafik plan aşağıdadır:

Yürütme planı

Ve işte .sqlplan dosyasına bir link .


İcra planına da bakabilir miyiz? :)
dezso

1
Ve lütfen gerçek yürütme planını (tahmin edilmeyen) .sqlplan uzantısıyla yayınlayın. Çoğu insan sadece grafik planın ekran görüntüsünü yayınlamak ister ve bu çok daha az yararlıdır.
Aaron Bertrand

Tamam bir yürütme planı ekledi yanı sıra SQL sorgusu güncellendi.
SandersKY

@SandersKY Soruyla ilgili her şeyi aynı sitede tutmak için .sqlplan dosyasını satır içine almak en iyisidir.
Trygve Laugstøl

1
@trygvis - Bu mesajlardaki uzunluk sınırlamaları nedeniyle çoğu zaman mümkün olmazdı. Utanç yığını değişimi, dahili olarak eklerin barındırılmasını desteklemez.
Martin Smith

Yanıtlar:


18

Aşağıdaki genel formdaki tablolar verilmiştir:

CREATE TABLE Device 
(
    ID integer PRIMARY KEY
);

CREATE TABLE EventType
(
    ID integer PRIMARY KEY, 
    Name nvarchar(50) NOT NULL
);

CREATE TABLE [Event]
(
    ID integer PRIMARY KEY, 
    [TimeStamp] datetime NOT NULL, 
    EventTypeID integer NOT NULL REFERENCES EventType, 
    DeviceID integer NOT NULL REFERENCES Device
);

Aşağıdaki dizin yararlıdır:

CREATE INDEX f1 
ON [Event] ([TimeStamp], EventTypeID) 
INCLUDE (DeviceID)
WHERE EventTypeID IN (2, 5, 7, 8, 9, 14);

Sorgu için:

SELECT
  [Event].ID,
  [Event].[TimeStamp],
  EventType.Name,
  Device.ID
FROM
  [Event]
INNER JOIN EventType ON EventType.ID = [Event].EventTypeID
INNER JOIN Device ON Device.ID = [Event].DeviceID
WHERE
  [Event].[TimeStamp] BETWEEN '2011-01-28' AND '2011-01-29'
  AND Event.EventTypeID IN (2, 5, 7, 8, 9, 14);

Filtre ANDyan tümce gereksinimini karşılar, dizinin ilk anahtarı [TimeStamp]filtrelenmiş bir aramaya izin verir EventTypeIDsve DeviceIDsütun dahil dizin kaplama yapar (çünkü tabloya DeviceIDbirleştirme için gereklidir Device).

Bitmiş plan

Dizinin ikinci anahtarı - EventTypeIDkesinlikle gerekli değildir (bir INCLUDEdsütun da olabilir ); Bunu dahil ettik anahtarında için burada belirtilen nedenlerden . Genel olarak, insanlara INCLUDEfiltrelenmiş bir dizin WHEREyan tümcesinden en az sütun eklemelerini öneririm .


Sorunun güncellenmiş sorgu ve yürütme planına dayanarak, EventTypeIDsAaron'un cevabında da belirtildiği gibi , filtrelenmiş listenin statik olmadığı sürece, SSMS tarafından önerilen daha genel endeksin burada daha iyi bir seçim olduğunu kabul ediyorum :

CREATE TABLE Device 
(
    ID integer PRIMARY KEY,
    Name nvarchar(50) NOT NULL UNIQUE
);

CREATE TABLE EventType
(
    ID integer PRIMARY KEY, 
    Name nvarchar(20) NOT NULL UNIQUE,
    [Description] nvarchar(100) NOT NULL
);

CREATE TABLE [Event]
(
    ID integer PRIMARY KEY, 
    PLCTimeStamp datetime NOT NULL,
    EventTypeID integer NOT NULL REFERENCES EventType, 
    DeviceID integer NOT NULL REFERENCES Device,
    IATA varchar(50) NOT NULL,
    Data1 integer NULL,
    Data2 integer NULL,
);

Önerilen dizin (uygunsa benzersiz olarak bildirin):

CREATE UNIQUE INDEX uq1
ON [Event]
    (EventTypeID, PLCTimeStamp)
INCLUDE 
    (DeviceID, IATA, Data1, Data2, ID);

Uygulama planından gelen kardinalite bilgileri (belgesiz sözdizimi, üretim sistemlerinde kullanılmaz):

UPDATE STATISTICS dbo.Event WITH ROWCOUNT = 4042700, PAGECOUNT = 400000;
UPDATE STATISTICS dbo.EventType WITH ROWCOUNT = 22, PAGECOUNT = 1;
UPDATE STATISTICS dbo.Device WITH ROWCOUNT = 2806, PAGECOUNT = 28;

Güncellenmiş sorgu ( tablo için INlistenin tekrarlanması, EventTypebu özel durumda optimize ediciye yardımcı olur):

SELECT
  Event.ID,
  Event.IATA,
  Device.Name,
  EventType.Description,
  Event.Data1,
  Event.Data2,
  Event.PLCTimeStamp,
  Event.EventTypeID
FROM
  Event
INNER JOIN EventType ON EventType.ID = Event.EventTypeID
INNER JOIN Device ON Device.ID = Event.DeviceID
WHERE
  Event.EventTypeID IN (3, 30, 40, 41, 42, 46, 49, 50)
  AND EventType.ID IN (3, 30, 40, 41, 42, 46, 49, 50)
  AND Event.PLCTimeStamp BETWEEN '2011-01-28' AND '2011-01-29'
  AND Event.IATA LIKE '%0005836217%'
ORDER BY Event.ID;

Tahmini yürütme planı:

İkinci plan

Tahmin ettiğiniz istatistikleri kullandığım için alacağınız plan muhtemelen farklı olacaktır. Genel nokta, optimize ediciye olabildiğince fazla bilgi vermek ve 4 milyon satır [Event]tablosunda etkili bir erişim yöntemi (dizin) sağlamaktır .


8

Maliyetin çoğunluğu kümelenmiş dizin taramasıdır ve bu tablo gerçekten geniş değilse veya çıktıdaki tüm sütunlara gerçekten ihtiyacınız yoksa, SQL Server'ın bu durumun mevcut senaryoda başka bir şey değişmeden en uygun yol olduğuna inanıyorum . İlgilendiği satır aralığını daraltmak için bir aralık taraması kullanır (CI araması olarak etiketlenir), ancak çıktı nedeniyle, oluşturduğunuz filtrelenmiş dizinle bile hala bir arama veya CI taraması gerektirecektir. bu aralığa hedeflenir ve bu durumda bile CI taraması muhtemelen en ucuzudur (veya en azından SQL Server bunu böyle tahmin eder).

Yürütme planı size bu dizinin yararlı olacağını söylüyor:

CREATE NONCLUSTERED INDEX ix_EventTypeID_PLCTimeStamp_WithIncludes
  ON [dbo].[Event] ([EventTypeID],[PLCTimeStamp])
  INCLUDE ([ID],[DeviceID],[Data1],[Data2],[IATA]);

Veri eğriliğinize bağlı olarak, bunun tersi daha iyi olabilir, örneğin:

CREATE NONCLUSTERED INDEX ix_PLCTimeStamp_EventTypeID_WithIncludes
  ON [dbo].[Event] ([PLCTimeStamp],[EventTypeID])
  INCLUDE ([ID],[DeviceID],[Data1],[Data2],[IATA]);

Ama her ikisinin de hangisinin daha iyi olduğundan emin olmak için test ederdim - ya bu dizinlerden herhangi biri ile şu an sahip olduğunuz arasındaki fark sadece marjinal olabilir (bilmemiz için çok fazla değişken) ve ek bir hesaba katmanız gerekir endeksi ekstra bakım gerektirir ve bu DML işlemlerinizi (ekleme / güncelleme / silme) belirgin şekilde etkileyebilir. @SQLKiwi tarafından önerildiği gibi bu dizine filtre ölçütlerini dahil etmeyi de düşünebilirsiniz , ancak yalnızca sık aradığınız EventTypeID değerleri kümesiyse . Bu küme zaman içinde değişirse, filtrelenmiş dizin yalnızca bu belirli sorgu için yararlı olacaktır.

Böyle düşük bir sıra sayısı ile, şu anda performansın ne kadar kötü olabileceğini merak etmeliyim? Bu sorgu 3 satır döndürür (ancak kaç satırı reddettiğine dair herhangi bir gösterge yoktur). Tabloda kaç satır var?


4

Sadece yürütme planını çalıştırdığımda SQL Server 2008 R2'nin aslında bir dizin önerisi yaptığını keşfettim. Bu önerilen dizin, sorgunun yaklaşık% 90 daha hızlı çalışmasını sağlar.

Önerdiği dizin şuydu:

CREATE NONCLUSTERED INDEX [INDEX_spBagSearch] ON [dbo].[Event] 
(
    [EventTypeID] ASC,
    [PLCTimeStamp] ASC
)
INCLUDE ( [ID],
[DeviceID],
[Data1],
[Data2],
[IATA]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO
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.