Farklı tablolardan ORDER BY ile TOP 1 seçildiğinde dizinlenmiş görünüm nasıl ayarlanır


11

Aşağıdaki sorgu iki kümelenmiş dizin taraması olmadan gerçekleştirir, böylece aşağıdaki senaryoda dizinli bir görünüm kurmak için mücadele ediyorum. Ne zaman bu sorgu için bir dizin görünümü oluşturmak ve sonra kullanmak, ben koymak herhangi bir dizin yoksay gibi görünüyor:

    -- +++ THE QUERY THAT I WANT TO IMPROVE PERFORMANCE-WISE +++

    SELECT TOP 1 *
    FROM    dbo.TB_test1 t1
            INNER JOIN dbo.TB_test2 t2 ON t1.PK_ID1 = t2.FK_ID1
    ORDER BY t1.somethingelse1
           ,t2.somethingelse2;


    GO

Tablo kurulumu aşağıdaki gibidir:

  • iki masa
  • yukarıdaki sorgu tarafından bir iç birleşim ile birleştirilirler
  • ve birinci sorgudan bir sütun ve daha sonra yukarıdaki sorgu tarafından ikinci tablodan bir sütunla sıralanmıştır; sadece TOP 1 seçildi
  • (aşağıdaki komut dosyasında, sorunun yeniden oluşturulmasına yardımcı olması durumunda test verileri oluşturmak için bazı satırlar da vardır)

    -- +++ TABLE SETUP +++
    
    CREATE TABLE [dbo].[TB_test1]
        (
         [PK_ID1] [INT] IDENTITY(1, 1)  NOT NULL
        ,[something1] VARCHAR(40) NOT NULL
        ,[somethingelse1] BIGINT NOT NULL
            CONSTRAINT [PK_TB_test1] PRIMARY KEY CLUSTERED ( [PK_ID1] ASC )
        );
    
    GO
    
    create TABLE [dbo].[TB_test2]
        (
         [PK_ID2] [INT] IDENTITY(1, 1)  NOT NULL
        ,[FK_ID1] [INT] NOT NULL
        ,[something2] VARCHAR(40) NOT NULL
        ,[somethingelse2] BIGINT NOT NULL
            CONSTRAINT [PK_TB_test2] PRIMARY KEY CLUSTERED ( [PK_ID2] ASC )
        );
    
    GO
    
    ALTER TABLE [dbo].[TB_test2]  WITH CHECK ADD  CONSTRAINT [FK_TB_Test1] FOREIGN KEY([FK_ID1])
    REFERENCES [dbo].[TB_test1] ([PK_ID1])
    GO
    
    ALTER TABLE [dbo].[TB_test2] CHECK CONSTRAINT [FK_TB_Test1]
    
    GO
    
    
    -- +++ TABLE DATA GENERATION +++
    
    -- this might not be the quickest way, but it's only to set up test data
    
    INSERT INTO dbo.TB_test1
            ( something1, somethingelse1 )
    VALUES  ( CONVERT(VARCHAR(40), NEWID())  -- something1 - varchar(40)
              ,ISNULL(ABS(CHECKSUM(NewId())) % 92233720368547758078, 1)   -- somethingelse1 - bigint
              )
    
    GO 100000
    
    RAISERROR( 'Finished setting up dbo.TB_test1', 0, 1) WITH NOWAIT    
    
    GO    
    
    INSERT INTO dbo.TB_test2
            ( FK_ID1, something2, somethingelse2 )
    VALUES  ( ISNULL(ABS(CHECKSUM(NewId())) % ((SELECT MAX(PK_ID1) FROM dbo.TB_test1) - 1), 0) + 1 -- FK_ID1 - int
              ,CONVERT(VARCHAR(40), NEWID())  -- something2 - varchar(40)
              ,ISNULL(ABS(CHECKSUM(NewId())) % 92233720368547758078, 1)   -- somethingelse2 - bigint
              )
    
    GO 100000
    
    RAISERROR( 'Finished setting up dbo.TB_test2', 0, 1) WITH NOWAIT          
    
    GO

Dizine eklenen görünüm muhtemelen aşağıdaki gibi tanımlanmalıdır ve sonuçta ortaya çıkan TOP 1 sorgusu aşağıdadır. Ancak bu sorgunun dizinlenmiş görünüm olmadan daha iyi performans göstermesi için hangi dizinlere ihtiyacım var?

    CREATE VIEW VI_test
    WITH SCHEMABINDING
    AS
        SELECT  t1.PK_ID1
               ,t1.something1
               ,t1.somethingelse1
               ,t2.PK_ID2
               ,t2.FK_ID1
               ,t2.something2
               ,t2.somethingelse2
        FROM    dbo.TB_test1 t1
                INNER JOIN dbo.TB_test2 t2 ON t1.PK_ID1 = t2.FK_ID1


    GO


    SELECT TOP 1 * FROM dbo.VI_test ORDER BY somethingelse1,somethingelse2


    GO

Yanıtlar:


12

Üzerine koyduğum herhangi bir dizini yok sayıyor gibi görünüyor

SQL Server Enterprise Edition'ı (veya eşdeğer olarak Deneme ve Geliştirici) WITH (NOEXPAND)kullanmadığınız sürece, kullanmak için görünüm referansında kullanmanız gerekir. Aslında, Enterprise kullanıyor olsanız bile, bu ipucunu kullanmanız için iyi nedenler vardır .

İpucu olmadan, sorgu iyileştirici (Enterprise Edition'da), materyalize edilmiş görünümü kullanma veya temel tablolara erişme arasında maliyet temelli bir seçim yapabilir. Görünümün temel tablolar kadar büyük olduğu durumlarda, bu hesaplama temel tabloları destekleyebilir.

Bir başka ilgi çekici nokta, NOEXPANDipucu olmadan , görünüm referanslarının optimizasyon başlamadan önce her zaman temel sorguya genişletilmesidir. Optimizasyon ilerledikçe, optimize edici önceki optimizasyon etkinliğine bağlı olarak genişletilmiş tanımı materyalize görünümle eşleştirebilir veya eşleştiremeyebilir. Basit sorgunuzda bu kesinlikle doğru değildir, ancak tamlık için söz ediyorum.

Bu nedenle, NOEXPANDtablo ipucunu kullanmak ana seçeneğinizdir, ancak yalnızca temel tablo anahtarlarını ve görünümde sipariş vermek için gereken sütunları gerçekleştirmeyi de düşünebilirsiniz. Birleşik anahtar sütunlarında benzersiz bir kümelenmiş dizin, ardından sipariş sütunlarında ayrı bir kümelenmemiş dizin oluşturun.

Bu, gerçekleşen görünümün boyutunu azaltır ve görünümü temel tablolarla senkronize tutmak için yapılması gereken otomatik güncelleme sayısını sınırlar. Sorgunuz daha sonra görünümden gerekli sırayla ilk 1 anahtarı almak için yazılabilir (ideal olarak ile NOEXPAND), sonra görünümdeki tuşları kullanarak kalan sütunları almak için temel tablolara geri katılın.

Başka bir varyasyon, sipariş sütunları ve tablo anahtarlarındaki görünümü kümelemek, ardından anahtarları kullanarak temel tablodan görünüm olmayan sütunları el ile almak için sorguyu yazmaktır. Sizin için en iyi seçenek daha geniş içeriğe bağlıdır. Karar vermenin iyi bir yolu, bunu gerçek veriler ve iş yükü ile test etmektir.

Temel çözüm

CREATE VIEW VI_test
WITH SCHEMABINDING
AS
    SELECT
        t1.PK_ID1,
        t1.something1,
        t1.somethingelse1,
        t2.PK_ID2,
        t2.FK_ID1,
        t2.something2,
        t2.somethingelse2
    FROM dbo.TB_test1 t1
    INNER JOIN dbo.TB_test2 t2 
        ON t1.PK_ID1 = t2.FK_ID1;
GO
-- Brute force unique clustered index
CREATE UNIQUE CLUSTERED INDEX cuq 
ON dbo.VI_test 
    (somethingelse1, somethingelse2, PK_ID1, PK_ID2);
GO
SELECT TOP (1) * 
FROM dbo.VI_test WITH (NOEXPAND)
ORDER BY somethingelse1,somethingelse2;

Yürütme planı:

Kaba kuvvet endeksi

Kümelenmemiş bir dizin kullanma

-- Minimal unique clustered index
CREATE UNIQUE CLUSTERED INDEX cuq 
ON dbo.VI_test 
    (PK_ID1, PK_ID2)
WITH (DROP_EXISTING = ON);
GO
-- Nonclustered index for ordering
CREATE NONCLUSTERED INDEX ix 
ON dbo.VI_test (somethingelse1, somethingelse2);

Yürütme planı:

Sipariş için kümelenmemiş dizin

Bu planda bir arama var, ancak sadece tek bir satır getirmek için kullanılır.

Minimum Dizine Alınmış Görünüm

ALTER VIEW VI_test
WITH SCHEMABINDING
AS
    SELECT
        t1.PK_ID1,
        t2.PK_ID2,
        t1.somethingelse1,
        t2.somethingelse2
    FROM dbo.TB_test1 t1
    INNER JOIN dbo.TB_test2 t2 
        ON t1.PK_ID1 = t2.FK_ID1;
GO
-- Unique clustered index
CREATE UNIQUE CLUSTERED INDEX cuq 
ON dbo.VI_test 
    (somethingelse1, somethingelse2, PK_ID1, PK_ID2);

Sorgu:

SELECT TOP (1)
    V.PK_ID1,
    TT1.something1,
    V.somethingelse1,
    V.PK_ID2,
    TT2.FK_ID1,
    TT2.something2,
    V.somethingelse2
FROM dbo.VI_test AS V WITH (NOEXPAND)
JOIN dbo.TB_test1 AS TT1 ON TT1.PK_ID1 = V.PK_ID1
JOIN dbo.TB_test2 AS TT2 ON TT2.PK_ID2 = V.PK_ID2
ORDER BY somethingelse1,somethingelse2;

Yürütme planı:

Son sorgu planı

Bu, geri alınan tablo anahtarlarını (sırayla kümelenmiş görünümden tek bir satır getirme) ve ardından kalan sütunları getirmek için temel tablolarda iki tek satırlı aramayı gösterir.

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.