Sürekli Tarama biriktirme


14

Birkaç düzine satır içeren bir masam var. Basitleştirilmiş kurulum takip ediyor

CREATE TABLE #data ([Id] int, [Status] int);

INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);

Ve ben bu tabloyu tablo değeri oluşturulmuş satırlar (değişkenler ve sabitler yapılmış) bir dizi, gibi bir sorgu var

DECLARE @id1 int = 101, @id2 int = 105;

SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (@id1, 'A'),
        (@id2, 'B')
    ) p([Id], [Code])
    FULL JOIN #data d ON d.[Id] = p.[Id];

Sorgu yürütme planı, FULL LOOP JOINher iki girdinin de çok az satırı olduğundan optimizasyon kararının uygun görünen stratejiyi kullanmak olduğunu gösteriyor . Fark ettiğim (ve kabul edemediğim) bir şey, TVC satırlarının biriktirilmesidir (kırmızı kutudaki yürütme planının alanına bakın).

Sürekli Tarama biriktirme

Neden optimizer makarayı buraya getiriyor, bunu yapmanın nedeni nedir? Makaranın ötesinde karmaşık bir şey yoktur. Gerekli olmadığı anlaşılıyor. Bu durumda ondan nasıl kurtulur, olası yollar nelerdir?


Yukarıdaki plan

Microsoft SQL Server 2014 (SP2-CU11) (KB4077063) - 12.0.5579.0 (X64)


Feedback.azure.com
i-one ile

Yanıtlar:


19

Neden optimizer makarayı buraya getiriyor, bunu yapmanın nedeni nedir? Makaranın ötesinde karmaşık bir şey yoktur.

Makaranın ötesindeki şey, sol birleştirme / anti yarı birleştirme alternatifi oluşturulduğunda basitçe çoğaltılabilen basit bir tablo referansı değildir .

Bu olabilir bakmak biraz bir tablo (Sabit Tarama) gibi ama bir olduğunu * optimizer için UNION ALLayrı satır VALUESmaddesi.

Ek karmaşıklık, optimize edicinin kaynak satırlarını biriktirmeyi ve yeniden oynatmayı seçmesi ve makarayı daha sonra basit bir "tablo alma" ile değiştirmemesi için yeterlidir. Örneğin, tam birleştirmeden gelen ilk dönüşüm şöyle görünür:

erken plan

Genel dönüşümün getirdiği ekstra makaralara dikkat edin. Basit bir tablo get yukarıdaki Makaralar kural tarafından sonradan temizlediğini SpoolGetToGet.

Optimize edicinin buna karşılık gelen bir SpoolConstGetToConstGetkuralı varsa, prensipte istediğiniz gibi çalışabilir.

Bu durumda ondan nasıl kurtulur, olası yollar nelerdir?

Gerçek bir tablo kullanın (geçici veya değişken) veya tam birleştirmeden elde edilen dönüşümü el ile yazın, örneğin:

WITH 
    p([Id], [Code]) AS
    (
        SELECT @id1, 'A'
        UNION ALL
        SELECT @id2, 'B'
    ),
    FullJoin AS
    (
        SELECT
            p.Code,
            d.[Status]
        FROM p
        LEFT JOIN #data d 
            ON d.[Id] = p.[Id]
        UNION ALL
        SELECT
            NULL,
            D.[Status]
        FROM #data AS D
        WHERE NOT EXISTS
        (
            SELECT *
            FROM p
            WHERE p.Id = D.Id
        )
    )
SELECT
    COALESCE(FullJoin.Code, 'X') AS Code,
    COALESCE(FullJoin.Status, 0) AS [Status]
FROM FullJoin;

Manuel yeniden yazma planı:

Manuel yeniden yazma planı

Bu, orijinal için 0.0203412 birim ile karşılaştırıldığında tahmini 0.0067201 birim maliyete sahiptir.


* Bu bir olarak görülebilir LogOp_UnionAlliçinde Dönüştürülmüş Ağacı (TF 8605). In Girdi Ağacı (TF 8606) bir olduğunu LogOp_ConstTableGet. Dönüştürülen Ağacı gösterileri ayrıştırma, normalleştirme, algebrization, bağlama ve diğer bazı hazırlık çalışmalarının ardından iyileştirici ifade elemanlarının ağacı. Girdi Ağacı Olumsuzluk Normal Form (NNF dönüştürme), çöken çalışma zamanı sabiti, ve diğer birkaç bit ve bobs dönüştürüldükten sonra elemanlarını göstermektedir. NNF dönüştürme, diğer şeylerin yanı sıra, mantıksal sendikaları ve ortak tabloları daraltmak için mantığı içerir.


3

Masa makarası, VALUESyan tümcedeki iki grup kümeden bir tablo oluşturur .

İlk olarak, bu değerleri geçici bir tabloya aşağıdaki gibi ekleyerek makarayı ortadan kaldırabilirsiniz:

DROP TABLE IF EXISTS #data;
CREATE TABLE #data ([Id] int, [Status] int);

INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);

DROP TABLE IF EXISTS #p;
CREATE TABLE #p
(
    Id int NOT NULL
    , Code char(1) NOT NULL
);

DECLARE @id1 int = 101, @id2 int = 105;

INSERT INTO #p (Id, Code)
VALUES
        (@id1, 'A'),
        (@id2, 'B');


SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM #p p
    FULL JOIN #data d ON d.[Id] = p.[Id];

Sorgunuz için yürütme planına baktığımızda, çıktı listesinin Unionönek kullanan iki sütun içerdiğini görüyoruz ; bu, makaraların bir birlikten bir tablo oluşturduğuna dair bir ipucudur:

resim açıklamasını buraya girin

FULL OUTER JOINDeğerleri erişmek için SQL Server gerektirir pkatılmak her "tarafı" için bir kez, iki kez. Bir biriktirme oluşturmak, elde edilen iç döngülerin birleştirilen verilere erişmesini sağlar.

İlginç bir şekilde, FULL OUTER JOINa LEFT JOINve a ile sonuçları RIGHT JOINve UNIONsonuçları birlikte değiştirirseniz SQL Server bir biriktirme kullanmaz.

SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (101, 'A'),
        (105, 'B')
    ) p([Id], [Code])
    LEFT JOIN #data d ON d.[Id] = p.[Id]
UNION
SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (101, 'A'),
        (105, 'B')
    ) p([Id], [Code])
    RIGHT JOIN #data d ON d.[Id] = p.[Id];

resim açıklamasını buraya girin

Not, UNIONyukarıdaki sorguyu kullanmanızı önermiyorum; daha büyük girdi setleri için, FULL OUTER JOINzaten sahip olduğunuz basitten daha verimli olmayabilir .


Gerçek iş yükünüzde, makara gerçekten bu kadar pahalı mı?
Max Vernon
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.