Cara Paling Efisien untuk Mengambil Kisaran Tanggal


16

Apa cara paling efisien untuk mengambil rentang tanggal dengan struktur tabel seperti ini?

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

Katakanlah Anda ingin rentang untuk keduanya StartDatedan EndDate. Jadi dengan kata lain, jika StartDatejatuh di antara @StartDateBegindan @StartDateEnd, dan EndDatejatuh di antara @EndDateBegindan @EndDateEnd, maka lakukan sesuatu.

Saya tahu ada beberapa cara untuk melakukan hal ini, tetapi apa yang paling disarankan?

Jawaban:


29

Ini adalah masalah yang sulit untuk dipecahkan secara umum, tetapi ada beberapa hal yang dapat kita lakukan untuk membantu pengoptimal memilih rencana. Script ini membuat tabel dengan 10.000 baris dengan distribusi baris pseudo-acak yang dikenal untuk menggambarkan:

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

Pertanyaan pertama adalah bagaimana cara mengindeks tabel ini. Salah satu opsi adalah menyediakan dua indeks pada DATETIMEkolom, sehingga pengoptimal setidaknya dapat memilih apakah akan mencari StartDateatau EndDate.

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

Tentu saja, ketidaksetaraan pada keduanya StartDatedan EndDateberarti bahwa hanya satu kolom di setiap indeks dapat mendukung pencarian dalam contoh query, tetapi ini adalah tentang yang terbaik yang bisa kita lakukan. Kami mungkin mempertimbangkan menjadikan kolom kedua di setiap indeks sebagai INCLUDEbukan kunci, tetapi kami mungkin memiliki kueri lain yang dapat melakukan pencarian kesetaraan pada kolom utama dan pencarian ketidaksetaraan pada kolom kedua. Juga, kita bisa mendapatkan statistik yang lebih baik dengan cara ini. Bagaimanapun...

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

Kueri ini menggunakan variabel, jadi secara umum pengoptimal akan menebak selektivitas dan distribusi, sehingga menghasilkan perkiraan kardinalitas 81 baris . Bahkan, kueri menghasilkan 2076 baris, perbedaan yang mungkin penting dalam contoh yang lebih kompleks.

Pada SQL Server 2008 SP1 CU5 atau lambat (atau R2 RTM CU1) kita dapat mengambil keuntungan dari Parameter Embedding Optimization untuk mendapatkan perkiraan yang lebih baik, hanya dengan menambahkan OPTION (RECOMPILE)ke SELECTpertanyaan di atas. Ini menyebabkan kompilasi sesaat sebelum batch dijalankan, memungkinkan SQL Server untuk 'melihat' nilai parameter nyata dan mengoptimalkannya. Dengan perubahan ini, perkiraan meningkat menjadi 468 baris (walaupun Anda perlu memeriksa paket runtime untuk melihatnya). Perkiraan ini lebih baik dari 81 baris, tetapi masih belum terlalu dekat. Ekstensi pemodelan yang diaktifkan oleh tanda jejak 2301 dapat membantu dalam beberapa kasus, tetapi tidak dengan kueri ini.

Masalahnya adalah di mana baris yang dikualifikasikan oleh dua rentang pencarian tumpang tindih. Salah satu asumsi penyederhanaan yang dibuat dalam komponen estimasi biaya dan kardinalitas optimizer adalah bahwa predikat independen (jadi jika keduanya memiliki selektivitas 50%, hasil penerapan keduanya diasumsikan memenuhi syarat 50% dari 50% = 25% dari baris ). Di mana korelasi semacam ini merupakan masalah, kita sering dapat mengatasinya dengan statistik multi-kolom dan / atau difilter. Dengan dua rentang dengan titik awal dan akhir yang tidak diketahui, ini menjadi tidak praktis. Di sinilah kadang-kadang kita harus menggunakan penulisan ulang kueri ke formulir yang menghasilkan perkiraan yang lebih baik:

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)

Bentuk ini terjadi untuk menghasilkan perkiraan runtime 2110 baris (dibandingkan 2076 aktual). Kecuali Anda memiliki TF 2301 pada, dalam hal ini teknik pemodelan yang lebih maju melihat melalui trik dan menghasilkan perkiraan yang persis sama seperti sebelumnya: 468 baris.

Suatu hari SQL Server mungkin mendapatkan dukungan asli untuk interval. Jika itu datang dengan dukungan statistik yang baik, pengembang mungkin takut rencana tuning kueri seperti ini sedikit kurang.


5

Saya tidak tahu solusi yang cepat untuk semua distribusi data, tetapi jika semua rentang Anda pendek, kami biasanya dapat mempercepatnya. Jika, misalnya, rentang lebih pendek dari satu hari, daripada permintaan ini:

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

kita dapat menambahkan satu syarat lagi:

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

Akibatnya, alih-alih memindai seluruh tabel, kueri hanya akan memindai rentang dua hari, yang lebih cepat. Jika rentang mungkin lebih panjang, kami dapat menyimpannya sebagai urutan yang lebih pendek. Detail di sini: Menyetel Pertanyaan SQL dengan Bantuan Kendala

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.