Hesaplanan Sütunda Filtrelenmiş Dizin oluşturulamıyor


18

Bir önceki soruda, bir tabloya yeni hesaplanmış sütunlar eklerken kilit yükselmesini devre dışı bırakmak iyi bir fikir mi? , Hesaplanmış bir sütun oluşturuyorum:

ALTER TABLE dbo.tblBGiftVoucherItem
ADD isUsGift AS CAST
(
    ISNULL(
        CASE WHEN sintMarketID = 2 
            AND strType = 'CARD'
            AND strTier1 LIKE 'GG%' 
        THEN 1 
        ELSE 0 
        END
    , 0) 
    AS BIT
) PERSISTED;

Hesaplanan sütun PERSISTEDve computed_column_definition (Transact-SQL) 'e göre :

KALICI

Veritabanı Altyapısının hesaplanan değerleri fiziksel olarak tabloda depolayacağını ve hesaplanan sütunun bağlı olduğu diğer sütunlar güncellendiğinde değerleri güncelleyeceğini belirtir. Hesaplanan bir sütunu PERSISTED olarak işaretlemek, hesaplanan bir sütunda belirleyici ancak kesin olmayan bir dizin oluşturulmasına izin verir. Daha fazla bilgi için, bkz. Hesaplanan Sütunlar hakkında Dizinler. Bölümlenmiş bir tablonun bölümleme sütunları olarak kullanılan tüm hesaplanmış sütunlar açıkça PERSISTED olarak işaretlenmelidir. computed_column_expression, PERSISTED belirtildiğinde belirleyici olmalıdır.

Ancak sütunumda bir dizin oluşturmaya çalıştığımda aşağıdaki hatayı alıyorum:

CREATE INDEX FIX_tblBGiftVoucherItem_incl
ON dbo.tblBGiftVoucherItem (strItemNo) 
INCLUDE (strTier3)
WHERE isUsGift = 1;

Filtre ifadesi 'FIX_tblBGiftVoucherItem_incl', 'dbo.tblBGiftVoucherItem' tablosunda oluşturulamaz çünkü filtre ifadesindeki 'isUsGift' sütunu hesaplanan bir sütundur. Filtre ifadesini bu sütunu içermeyecek şekilde yeniden yazın.

Hesaplanan bir sütunda nasıl filtrelenmiş bir dizin oluşturabilirim?

veya

Alternatif bir çözüm var mı?


3
Üzerinde filtre uygulanmış bir dizin oluşturabilirsiniz WHERE (sintMarketID = 2 AND strType = 'CARD' AND strTier1 LIKE 'GG%').
ypercubeᵀᴹ

Yanıtlar:


21

Ne yazık ki SQL Server 2014'ten itibaren, Filtered Indexbir Hesaplanmış Sütun üzerinde bir Filtre oluşturma yeteneği yoktur (kalıcı olup olmadığına bakılmaksızın).

2009'dan beri açık bir Bağlantı Öğesi var , bu yüzden lütfen devam edin ve oy verin. Belki Microsoft bunu bir gün düzeltir.

Aaron Bertrand'ın Filtrelenmiş Endekslerle ilgili bir dizi başka konuyu kapsayan bir makalesi vardır .


21

Kalıcı bir sütunda filtrelenmiş bir dizin oluşturamamanıza rağmen, kullanabileceğiniz oldukça basit bir geçici çözüm vardır.

Bir test olarak, bir IDENTITYsütun ve kimlik sütununu temel alan kalıcı bir hesaplanmış sütun içeren basit bir tablo oluşturdum :

USE tempdb;

CREATE TABLE dbo.PersistedViewTest
(
    PersistedViewTest_ID INT NOT NULL
        CONSTRAINT PK_PersistedViewTest
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , SomeData VARCHAR(2000) NOT NULL
    , TestComputedColumn AS (PersistedViewTest_ID - 1) PERSISTED
);
GO

Sonra, hesaplanan sütunda bir filtre ile tabloya dayalı şemaya bağlı bir görünüm oluşturdum:

CREATE VIEW dbo.PersistedViewTest_View
WITH SCHEMABINDING
AS
SELECT PersistedViewTest_ID
    , SomeData 
    , TestComputedColumn
FROM dbo.PersistedViewTest
WHERE TestComputedColumn < CONVERT(INT, 27);

Daha sonra, hesaplanan sütunun değeri de dahil olmak üzere görünümde depolanan değerlere devam etme etkisi olan şemaya bağlı görünüm üzerinde kümelenmiş bir dizin oluşturdum:

CREATE UNIQUE CLUSTERED INDEX IX_PersistedViewTest
ON dbo.PersistedViewTest_View(PersistedViewTest_ID);
GO

Tabloya bazı test verileri ekleyin:

INSERT INTO dbo.PersistedViewTest (SomeData)
SELECT o.name + o1.name + o2.name
FROM sys.objects o
    CROSS JOIN sys.objects o1
    CROSS JOIN sys.objects o2;

Görünümde bir istatistik öğesi ve bir dizin oluşturun:

CREATE STATISTICS ST_PersistedViewTest_View
ON dbo.PersistedViewTest_View(TestComputedColumn)
WITH FULLSCAN;

CREATE INDEX IX_PersistedViewTest_View_TestComputedColumn
ON dbo.PersistedViewTest_View(TestComputedColumn);

Sahne SELECTkalıcı sütun ile masaya karşı ifadeleri olabilir Sorgu iyileştirici bunu yapmak mantıklı belirlerse artık otomatik, kalıcı görünümü kullanın:

SELECT pv.PersistedViewTest_ID
    , pv.TestComputedColumn
FROM dbo.PersistedViewTest pv
WHERE pv.TestComputedColumn = CONVERT(INT, 26)

Yukarıdaki sorgu için gerçek yürütme planı, sorgu optimize edicinin sonuçları döndürmek için kalıcı görünümü kullanmayı seçtiğini gösterir:

resim açıklamasını buraya girin

WHEREYukarıdaki fıkradaki açık dönüşümü fark etmiş olabilirsiniz . Bu açık CONVERT(INT, 26)sorgu iyileştiricisinin sorgu tarafından döndürülecek satır sayısını tahmin etmek için istatistik nesnesini doğru şekilde kullanmasına izin verir. Sorguyu ile yazarsak WHERE pv.TestComputedColumn = 26, sorgu optimize edici satır sayısını düzgün tahmin edemeyebilir, çünkü 26 aslında bir TINY INT; bu SQL Server'ın kalıcı görünümü kullanmamasına neden olabilir. Örtük dönüşümler çok acı verici olabilir ve karşılaştırmalar ve birleştirmeler için sürekli olarak doğru veri türlerini kullanmak gerekir.

Tabii ki, şema bağlamanın kullanılmasından kaynaklanan tüm standart "gotcha'lar" yukarıdaki senaryoya uygulanır; bu, bu geçici çözümün tüm senaryolarda kullanılmasını engelleyebilir. Örneğin, önce şema bağını görünümden kaldırmadan temel tabloyu değiştirmek artık mümkün olmayacaktır. Bunu yapmak için, kümelenmiş dizini görünümden kaldırmanız gerekir.

SQL Server Enterprise Edition'a sahip değilseniz, sorgu optimize edici, WITH (NOEXPAND)ipucu kullanarak doğrudan görünüme başvurmayan sorgular için kalıcı görünümü otomatik olarak kullanmaz . Enterprise Edition olmayan sürümlerde kalıcı görünümü kullanmanın avantajını anlamak için yukarıdaki sorguyu aşağıdaki gibi bir şeye yeniden yazmanız gerekir:

SELECT pv.PersistedViewTest_ID
    , pv.TestComputedColumn
FROM dbo.PersistedViewTest_View pv WITH (NOEXPAND)
WHERE pv.TestComputedColumn = CONVERT(INT, 26)

Yukarıdaki Enterprise Edition sınırlamasını işaret ettiği için Ian Ringrose'a ve ipucu için Paul White'a teşekkürler (NOEXPAND).

Paul'un bu cevabı , sürekli görünümlerle ilgili olarak sorgu optimize edici hakkında bazı ilginç ayrıntılara sahiptir.


Etraftaki çözüm, görünümde hem kümelenmiş bir dizin hem de kümelenmemiş bir dizin oluşturulduğunu gösterir. Kümelenmemiş dizin bir nedenle kümelenmiş dizin üzerinde kullanılmalı mı? Yoksa, kümelenmemiş endeks daha mı performans gösterir? Kümelenmiş dizin sorguda kullanılmış olsaydı, istatistikler ne gösterirdi?
Bob Bryan

İlginç bir soru, @BobBryan - aslında benzersiz bir dizin olması gerekmese de, görünümün kalıcı olmasını sağlamak için kümelenmiş dizin gereklidir. Bunun yerine görünümün kümelenmiş dizinini başka bir sütunda oluşturabilirdim TestComputedColumn. Ancak, kümelenmiş dizin tablo / görünüm için tüm verileri içerdiğinden, kümelenme anahtarı olarak monoton olarak artan bir sayı kullanmanın daha iyi olacağına karar verdim. Not, aslında bu varsayımı test etmedim ve aslında repro'nun bazı varyasyonları için yanlış olabilir.
Max Vernon

Kümelenmemiş dizin bir kaplama dizini değildir ve bu nedenle görünümden veya temel alınan tablodan sütunları filtreleyen, birleştiren veya döndüren herhangi bir sorgu, temel tabloya karşı bir anahtar arama işlemi gerçekleştirmelidir. görünüm. Gerçek dünya senaryosu için, cevabımın sınırlı kapsamının akılda daha iyi bir performansla ortaya konması muhtemeldir.
Max Vernon

4

Kimden Create Indexve wherehükmünden, bu mümkün değildir:

NEREDE

Dizine hangi satırların ekleneceğini belirterek filtrelenmiş bir dizin oluşturur. Filtrelenen dizin, bir tabloda kümelenmemiş bir dizin olmalıdır. Filtrelenmiş dizindeki veri satırları için filtrelenmiş istatistikler oluşturur.

Filtre yüklemi basit karşılaştırma mantığı kullanır ve hesaplanan bir sütuna, bir UDT sütununa, bir uzamsal veri türü sütununa veya bir hiyerarşi kimliği veri türü sütununa başvuramaz. NULL değişmezlerini kullanan karşılaştırmalara karşılaştırma işleçleriyle izin verilmez. Bunun yerine IS NULL ve IS NOT NULL işleçlerini kullanın.

Kaynak: MSDN


3
  • Filtrelenmiş dizini koymak için hesaplanmamış bir sütuna ihtiyacınız var.
  • Bu sütuna gitmek için değeri hesaplamanız gerekir.

Sütunları hesaplamadan önce, satır her değiştirildiğinde veya eklendiğinde sütun değerini hesaplamak için tetikleyiciler kullandık .

(Daha sonra sorgularda kullanılan 2. tablodan öğenin PK'sini eklemek / kaldırmak için bir tetikleyici de kullanılabilir.)


3

Bu, Max Vernon'un çalışmalarını geliştirme çabasıdır . Çözümünde, görünümde 2 dizin ve bir istatistik nesnesi kullanmanızı önerir.

1. dizin kümelenir, bu da bir tablodaki kümelenmemiş bir dizinin aksine, görünümde kümelenmemiş bir dizinin oluşturulmasına ilk önce kümelenmiş bir dizin olmadan denenirse bir hata üretilir.

2. dizin, sorgunun arkasındaki dizin olarak kullanılan kümelenmemiş bir dizindir. Cevabının yorumlar bölümünde, kümelenmemiş bir dizin yerine kümelenmiş bir dizin kullanılırsa ne olacağını sordum.

Aşağıdaki analiz bu soruya cevap vermeye çalışmaktadır.

Görünümünde kümelenmemiş bir dizin oluşturmuyorum dışında, aynı kodu kullanıyorum.

Ayrıca bir istatistik nesnesi oluşturmuyorum. Aşağıdaki kodu izlemek için SQL Server Management Studio'yu (SSMS) takip ediyorsanız ve kullanıyorsanız, hatalara benzeyen bazı kırmızı dalgalı çizgiler gördüğünüzün farkında olmalısınız. Bunlar (muhtemelen) hata değildir, ancak akıllıca bir konuyu içerir.

Intellisense'i devre dışı bırakabilir veya sadece hataları yoksayabilir ve komutları çalıştırabilirsiniz. Hatasız tamamlamaları gerekir.

-- Create the test table that uses a computed column.
USE tempdb;
CREATE TABLE dbo.PersistedViewTest
(
    PersistedViewTest_ID INT NOT NULL
    CONSTRAINT PK_PersistedViewTest
    PRIMARY KEY CLUSTERED
    IDENTITY(1,1)
    , SomeData VARCHAR(2000) NOT NULL
    , TestComputedColumn AS (PersistedViewTest_ID - 1) PERSISTED
);
GO

-- Insert some test data into the table.
INSERT INTO dbo.PersistedViewTest (SomeData)
SELECT o.name + o1.name + o2.name
FROM sys.objects o
    CROSS JOIN sys.objects o1
    CROSS JOIN sys.objects o2;
GO

Aşağıdaki sorgu planı (görünüm / dizin görünümü olmadan), aşağıdaki sorgu tabloya karşı çalıştırıldıktan sonra üretilir:

SELECT pv.PersistedViewTest_ID, pv.TestComputedColumn
FROM dbo.PersistedViewTest pv
WHERE pv.TestComputedColumn = CONVERT(INT, 26)
GO

resim açıklamasını buraya girin

Bu karşılaştırmak için bir taban çizgisi verir. Sorgu tamamlandıktan sonra bir istatistik nesnesi oluşturulduğuna dikkat edin (_WA_Sys_00000003_1FCDBCEB). Kümelenmiş tablo dizini oluşturulduğunda PK_PersistedViewTest istatistik nesnesi oluşturuldu.

Ardından, o görünümde filtrelenmiş görünüm ve kümelenmiş dizin oluşturulur:

-- Create filtered view on the computed column.
CREATE VIEW dbo.PersistedViewTest_View
WITH SCHEMABINDING
AS
SELECT PersistedViewTest_ID, SomeData, TestComputedColumn
FROM dbo.PersistedViewTest
WHERE TestComputedColumn < CONVERT(INT, 27);
GO

-- Create unique clustered index to persist the values, including the computed column.
CREATE UNIQUE CLUSTERED INDEX IX_PersistedViewTest
ON dbo.PersistedViewTest_View(PersistedViewTest_ID);
GO

Şimdi, sorguyu tekrar çalıştırmayı deneyelim, ancak bu sefer görünüme karşı:

SELECT pv.PersistedViewTest_ID, pv.TestComputedColumn
FROM dbo.PersistedViewTest_View pv
WHERE pv.TestComputedColumn = CONVERT(INT, 26)
GO

Yeni yürütme planı şimdi:

resim açıklamasını buraya girin

Yeni plana inanılırsa, görünümün ve bu görünümde kümelenmiş dizinin eklenmesinden sonra, istatistikler sorguyu yürütmek için gereken sürenin iki katına çıktığını gösterir. Ayrıca, sorgu çalıştırıldıktan sonra tablodaki sorgudan farklı olan yeni dizini desteklemek için yeni bir istatistik nesnesinin oluşturulmadığına dikkat edin.

Sorgu planı, hâlâ kümelenmemiş bir dizin oluşturmanın, sorgunun performansını artırmada oldukça yardımcı olacağını göstermektedir. Bu, istenen performans iyileştirmesi sağlanmadan önce görünüme kümelenmemiş bir dizin eklenmesi gerektiği anlamına mı geliyor? Denenecek son bir şey var. "İLE NOEXPAND" seçeneğini kullanmak için sorguyu değiştirin:

SELECT pv.PersistedViewTest_ID, pv.TestComputedColumn
FROM dbo.PersistedViewTest_View pv WITH (NOEXPAND)
WHERE pv.TestComputedColumn = CONVERT(INT, 26)
GO

Bu, aşağıdaki sorgu planıyla sonuçlanır:

resim açıklamasını buraya girin

Bu yürütme planı, Max Vernon'un cevabında verilen kümelenmemiş endeks ile üretilen plana oldukça benziyor. Ancak, bu bir daha az (kümelenmemiş) dizin ve bir daha az istatistik nesnesiyle yapılır.

Dizine alınmış bir görünümden doğru şekilde yararlanmak için NOEXPAND seçeneğinin SQL Server'ın hızlı ve standart sürümleriyle kullanılması gerektiği ortaya çıktı. Paul White, NOEXPAND seçeneğini kullanmanın yararlarını anlatan mükemmel bir makaleye sahiptir. Ayrıca bu seçeneğin, görünüm dizinleri tarafından sağlanan benzersiz garantinin optimize edici tarafından kullanılmasını sağlamak için kurumsal sürümle birlikte kullanılmasını önerir .

Yukarıdaki analiz, SQL Sever 2014'ün ekspres sürümü ile yapıldı. SQL Server 2016'nın geliştirici sürümü ile de denedim. Performans kazanımlarını elde etmek için NOEXPAND seçeneğinin geliştirme sürümünde gerekli olmadığı görülüyor, ancak yine de önerilir .

5 aydan daha kısa bir süre önce Microsoft, geliştirici sürümlerini ücretsiz hale getirdi . Lisans, kullanımı yalnızca geliştirmeyle sınırlar, yani veritabanı bir üretim ortamında kullanılamaz. Bu nedenle, bellek için optimize edilmiş tabloları, şifrelemeyi, R'yi vb. Test etmek istiyorsanız, artık lisanssız mazeretin yok. Birkaç gün önce yanımda SQL Server 2014 Express ile sorunsuz bir şekilde bilgisayarıma başarıyla yükledim.

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.