adakah pekerjaan untuk ketika Anda ingin menempatkan OR di dalam indeks yang difilter?

8

adakah pekerjaan untuk ketika Anda ingin menempatkan OR di dalam indeks yang difilter?

create index FIDX_tblbOrders_sdtmOrdCreated_INCL 
on dbo.tblBOrder(sdtmOrdCreated)
INCLUDE (sintMarketID,
         strCurrencyCode,
         sintOrderStatusID
         )
WHERE ((sintMarketId=1)
AND ( (sintOrderStatusId < 9) OR (sintOrderStatusId > 14)))

Saya mencoba membuat indeks di atas, karena saya TIDAK tertarik pada situasi di mana sintOrderStatusId IN (9-14)

Tentu saja saya dapat membuat tampilan atau tampilan yang diindeks, tetapi saya berusaha menghindarinya.

hanya menambahkan lebih banyak info: sintOrderStatusId adalah smallint NOT NULL dan nilai yang mungkin berkisar dari 1 hingga 30. 9 hingga 14 harus dihindari, oleh karena itu indeks yang difilter.

Marcello Miorelli
sumber

Jawaban:

12

Sayangnya, sepertinya tidak ada cara untuk membuat filter negatif untuk indeks, tanpa menggunakan tampilan yang terwujud. Jika tidak mungkin untuk membuat filter negatif seperti yang Anda inginkan, itu akan cukup sulit untuk query-optimizer untuk "memilih" indeks untuk digunakan, secara drastis meningkatkan waktu yang diperlukan untuk menemukan sebuah rencana yang baik.

Bergantung pada pola kueri untuk tabel ini, Anda cukup membuat dua indeks; satu untuk kurang dari 9 dan satu untuk lebih besar dari 14. Salah satu dari indeks ini dapat dipilih oleh pengoptimal permintaan untuk WHEREklausa sederhana sepertiWHERE StatusID = 6

CREATE TABLE dbo.TestNegativeFilter
(
    TestNegativeFilter INT NOT NULL
        CONSTRAINT PK_TestNegativeFilter
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , StatusID INT NOT NULL
);
GO

CREATE INDEX IX_TestNagativeFilter_LessThan9
ON dbo.TestNegativeFilter(StatusID)
WHERE (StatusID < 9);

CREATE INDEX IX_TestNagativeFilter_GreaterThan14
ON dbo.TestNegativeFilter(StatusID)
WHERE (StatusID > 14);

Cara lain untuk mencapai hal ini mungkin:

CREATE INDEX IX_TestNegativeFilter_9_to_14
ON dbo.TestNegativeFilter(StatusID)
WHERE (StatusID IN (9, 10, 11, 12, 13, 14));

SELECT *
FROM dbo.TestNegativeFilter tnf
EXCEPT
SELECT *
FROM dbo.TestNegativeFilter tnf
WHERE tnf.StatusID IN (9, 10, 11, 12, 13, 14);

Ini menggunakan indeks yang difilter pada 9 hingga 14 untuk mengecualikan baris.

Di rig pengujian saya, indeks penutup sederhana mengembalikan baris sejauh yang tercepat:

CREATE NONCLUSTERED INDEX IX_TestNegativeFilter_StatusID
ON dbo.TestNegativeFilter(StatusID)
INCLUDE (TestNegativeFilter);

SELECT *
FROM dbo.TestNegativeFilter tnf
WHERE tnf.StatusID NOT IN (9, 10, 11, 12, 13, 14);

Atau, gunakan variasi pada pendekatan yang digunakan dalam jawaban Anda sendiri :

CREATE INDEX [IX dbo.TestNegativeFilter StatusID not 9-14]
ON dbo.TestNegativeFilter (StatusID)
WHERE StatusID <> 9
AND StatusID <> 10
AND StatusID <> 11
AND StatusID <> 12
AND StatusID <> 13
AND StatusID <> 14;

Meskipun filter ditulis sebagai konjungsi, ia mendukung kueri yang ditulis dengan salah satu cara berikut (yang pertama menjadi sedikit lebih efisien):

  • StatusID NOT IN (9, 10, 11, 12, 13, 14)
  • StatusID < 9 OR StatusID > 14
  • StatusID NOT BETWEEN 9 AND 14
Max Vernon
sumber
1

tidak bagus, tetapi tampaknya berfungsi:

create index FIDX_tblbOrders_sdtmOrdCreated_INCL 
on dbo.tblBOrder(sdtmOrdCreated)
INCLUDE (sintMarketID,
         strCurrencyCode,
         sintOrderStatusID
         )
WHERE ((sintMarketId=1)
AND ( sintOrderStatusId IN (0,1,2,3,4,5,6,7,8,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30)))

Ketika saya mencoba melakukannya dengan cara yang lebih baik, ia tidak menyukainya:

masukkan deskripsi gambar di sini

Marcello Miorelli
sumber
1
Menurut dokumentasi MSND: "Indeks yang difilter didefinisikan pada satu tabel dan hanya mendukung operator perbandingan sederhana. Jika Anda memerlukan ekspresi filter yang merujuk beberapa tabel atau memiliki logika yang kompleks, Anda harus membuat tampilan." msdn.microsoft.com/en-us/library/cc280372.aspx
Paweł Tajs