Tarih Aralıklarını Almanın En Verimli Yolu


16

Böyle bir tablo yapısıyla tarih aralıklarını almanın en etkili yolu nedir?

create table SomeDateTable
(
    id int identity(1, 1) not null,
    StartDate datetime not null,
    EndDate datetime not null
)
go

Her ikisi için de bir aralık istediğinizi StartDateve EndDate. Yani başka bir deyişle, eğer StartDatearasına düşer @StartDateBeginve @StartDateEndve EndDatearasında kalan @EndDateBeginve @EndDateEnddaha sonra bir şey yapmak.

Bu konuda muhtemelen birkaç yol olduğunu biliyorum, ama en çok tavsiye edilen nedir?

Yanıtlar:


29

Bu genel olarak çözülmesi zor bir sorundur, ancak optimize edicinin bir plan seçmesine yardımcı olmak için yapabileceğimiz birkaç şey vardır. Bu komut dosyası, gösterilecek 10.000 sıralı bilinen bir sözde rastgele dağılım tablosu içeren bir tablo oluşturur:

CREATE TABLE dbo.SomeDateTable
(
    Id          INTEGER IDENTITY(1, 1) PRIMARY KEY NOT NULL,
    StartDate   DATETIME NOT NULL,
    EndDate     DATETIME NOT NULL
);
GO
SET STATISTICS XML OFF
SET NOCOUNT ON;
DECLARE
    @i  INTEGER = 1,
    @s  FLOAT = RAND(20120104),
    @e  FLOAT = RAND();

WHILE @i <= 10000
BEGIN
    INSERT dbo.SomeDateTable
        (
        StartDate, 
        EndDate
        )
    VALUES
        (
        DATEADD(DAY, @s * 365, {d '2009-01-01'}),
        DATEADD(DAY, @s * 365 + @e * 14, {d '2009-01-01'})
        )

    SELECT
        @s = RAND(),
        @e = RAND(),
        @i += 1
END

İlk soru bu tablonun nasıl endeksleneceğidir. Seçeneklerden biri DATETIMEsütunlara iki dizin sağlamaktır , böylece optimize edici en azından StartDateveya üzerinde arama yapmayı seçebilir EndDate.

CREATE INDEX nc1 ON dbo.SomeDateTable (StartDate, EndDate)
CREATE INDEX nc2 ON dbo.SomeDateTable (EndDate, StartDate)

Doğal olarak, hem de eşitsizlikler StartDateve EndDateher endekste sadece bir sütun, bir destek olabilir ortalama örnek sorguda aramak, ancak bu yapabileceğimiz en iyisi bu. Her dizindeki ikinci sütunu INCLUDEbir anahtar yerine bir anahtar haline getirebiliriz, ancak önde gelen sütunda eşitlik araması ve ikinci sütunda eşitsizlik araması gerçekleştirebilecek başka sorgularımız olabilir. Ayrıca, bu şekilde daha iyi istatistikler elde edebiliriz. Neyse ...

DECLARE
    @StartDateBegin DATETIME = {d '2009-08-01'},
    @StartDateEnd DATETIME = {d '2009-10-15'},
    @EndDateBegin DATETIME = {d '2009-08-05'},
    @EndDateEnd DATETIME = {d '2009-10-22'}

SELECT
    COUNT_BIG(*)
FROM dbo.SomeDateTable AS sdt
WHERE
    sdt.StartDate BETWEEN @StartDateBegin AND @StartDateEnd
    AND sdt.EndDate BETWEEN @EndDateBegin AND @EndDateEnd

Bu sorgu değişkenleri kullanır, bu nedenle genel olarak optimizatör seçicilik ve dağılımı tahmin eder, bu da 81 satırlık tahmini bir kardinalite tahmini ile sonuçlanır . Aslında, sorgu daha karmaşık bir örnekte önemli olabilecek bir tutarsızlık olan 2076 satır üretir.

SQL Server 2008 SP1 CU5 veya sonraki sürümlerinde (veya R2 RTM CU1) Parametre Gömme Optimizasyonu basitçe ekleyerek, daha iyi tahminler almak için OPTION (RECOMPILE)için SELECTyukarıdaki sorguda. Bu, toplu işlem yürütülmeden hemen önce bir derlemeye neden olarak SQL Server'ın gerçek parametre değerlerini 'görmesini' ve bunlar için optimize etmesini sağlar. Bu değişiklikle, tahmin 468 satıra yükselir (bunu görmek için çalışma zamanı planını kontrol etmeniz gerekir). Bu tahmin 81 satırdan daha iyidir, ancak yine de hepsi bu kadar yakın değildir. İzleme bayrağı 2301 tarafından etkinleştirilen modelleme uzantıları bazı durumlarda yardımcı olabilir, ancak bu sorguda yardımcı olmaz.

Sorun, iki aralık araması tarafından nitelenen satırların çakıştığıdır. Optimize edicinin maliyetleme ve kardinalite tahmin bileşeninde yapılan basitleştirici varsayımlardan biri, tahminlerin bağımsız olmasıdır (her ikisinin de% 50'lik bir seçiciliği varsa, her ikisini de uygulamanın sonucunun% 50'nin% 50'sini = satırların% 25'ini nitelediği varsayılır. ). Bu tür bir korelasyonun bir sorun olduğu durumlarda, genellikle çok sütunlu ve / veya filtrelenmiş istatistiklerle çalışabiliriz. Başlangıç ​​ve bitiş noktaları bilinmeyen iki aralık ile bu pratik değildir. Bu, bazen daha iyi bir tahmin oluşturmak için sorguyu yeniden yazmak için başvurmak zorunda olduğumuz yerdir:

SELECT COUNT(*) FROM
(
    SELECT
        sdt.Id
    FROM dbo.SomeDateTable AS sdt
    WHERE 
        sdt.StartDate BETWEEN @StartDateBegin AND @StartDateEnd
    INTERSECT
    SELECT
        sdt.Id
    FROM dbo.SomeDateTable AS sdt 
    WHERE
        sdt.EndDate BETWEEN @EndDateBegin AND @EndDateEnd
) AS intersected (id)
OPTION (RECOMPILE)

Bu form, 2110 satırlık bir çalışma zamanı tahmini üretir (gerçek 2076'ya göre). TF 2301'iniz açık değilse, bu durumda daha gelişmiş modelleme teknikleri hile yoluyla görür ve daha önce olduğu gibi aynı tahmini üretir: 468 satır.

Bir gün SQL Server, aralıklarla yerel destek alabilir. Bu, iyi bir istatistiksel destekle geliyorsa, geliştiriciler bu gibi sorgu planlarını biraz daha az korkutabilir.


5

Tüm veri dağıtımları için hızlı bir çözüm bilmiyorum, ancak tüm aralıklarınız kısaysa, genellikle hızlandırabiliriz. Örneğin, aralıklar bu sorgu yerine bir günden daha kısaysa:

SELECT  TaskId ,    
        TaskDescription ,
        StartedAt ,    
        FinishedAt    
FROM    dbo.Tasks    
WHERE   '20101203' BETWEEN StartedAt AND FinishedAt

bir koşul daha ekleyebiliriz:

SELECT  TaskId ,    
        TaskDescription ,
        StartedAt ,    
        FinishedAt    
FROM    dbo.Tasks    
WHERE   '20101203' BETWEEN StartedAt AND FinishedAt
    AND StartedAt >= '20101202'
    AND FinishedAt <= '20101204' ;

Sonuç olarak, tüm tabloyu taramak yerine sorgu yalnızca iki günlük aralığı tarar; bu daha hızlıdır. Aralıklar daha uzun olabilirse, bunları daha kısa olanların dizileri olarak saklayabiliriz. Ayrıntılar burada: Kısıtlamalar Yardımı ile SQL Sorgularını Ayarlama

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.