SQL Server kardinalite ipucu


14

Bir SQL Server optimizer (herhangi bir sürüm) için bir kardinalite tahmini 'enjekte' bir yolu var mı?

yani Oracle'ın kardinalite ipucuna benzer bir şey.

Motivasyonum Sorgu İyileştiricileri Gerçekten Ne Kadar İyi? [1] , burada kardinalite tahmin edicisinin kötü bir plan seçimi üzerindeki etkisini test ettiler. Bu nedenle, SQL Server'ı karmaşık sorgular için kesin olarak 'tahmin' etmeye zorlayabilirsem yeterli olur.


[1] Leis, Viktor ve diğ. "Sorgu iyileştiriciler gerçekten ne kadar iyi?"
VLDB Bağışları 9.3 (2015): 204-215.

Yanıtlar:


10

CARDINALITYStratejik olarak Oracle Mac'in ipucuna benzer bir şey TOPve MANY() Adam Machanic tarafından geliştirilen kullanıcı tanımlı bir işlev alabilirsiniz . Birkaç örnek üzerinde çalışalım. Serbestçe kullanılabilen AdventureWorks veritabanını kullanıyorum. Gerçekten thaşağıdaki sorgu türetilmiş tablo tarafından döndürülen satır sayısını denetlemek gerektiğini varsayalım :

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
) th ON p.ProductID = th.ProductID;

Olduğu gibi, 113443 satır tahmini alıyorum:

başlangıç ​​sorgusu

Eğer tahmin düşürmek gerekirse thben bir satır hedefi ayarlamak TOPiçin OPTIMIZE FORsorgu ipucu ile birlikte kullanabilirsiniz . İşte bunu yapmanın bir yolu:

DECLARE @row_goal BIGINT = 9223372036854775807;
SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (@row_goal) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
) th ON p.ProductID = th.ProductID
OPTION (OPTIMIZE FOR (@row_goal = 1));

Tahminin sadece 1 satır olduğunu görebiliriz:

1 satır tahmini

Sonuçları değiştirmekten kaçınmak için @row_goalmümkün olan en büyük BIGINTdeğere ayarladım . OPTIMIZE FORSanki sorgu ipucu sorguya iyileştirici için iyileştiricisi @row_goalis 1'e eşit aynı sonuçları elde edecek ancak sorgu farklı bir şekilde optimize edilecektir.

Bir kardinalite tahmininin artırılması daha zordur. TOPOptimize edici, yeterli satır döndürmeyeceğini anlayacağı için değerini artıramayız . Ancak, MANY()tahmine satır eklemek için bu işlevi kullanabiliriz . Not MANY()bu giriş parametresi ile değişir gelen işlevi, her zaman 0 satır ancak bu satır tahmin dönecektir. Türetilmiş tablodan satır tahminini 10 kat artırmanız gerektiğini varsayalım. Bunu başarmanın bir yolu:

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (9223372036854775807) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
    LEFT OUTER JOIN dbo.Many(10) AS m ON 1=1
) th ON p.ProductID = th.ProductID;

Tahminin 10 kat temel tablo olduğunu görebiliriz:

10X sorgusu

TOPOptimize edicinin tabloları hareket ettirmesini önlemek için gereksiz eklenmiştir. Bu olmadan MANY()işlev plandaki yanlış yere uygulanabilir.

Satır sayısını bir faktörle çarpmak yerine kesin bir fazla tahmin yapmak istiyorsanız iki tekniği birleştirmek mümkündür. Örneğin, türetilmiş tablonun tahmininin tam olarak 1000000 satır olması gerektiğini varsayalım. Bunu başarmanın bir yolu:

DECLARE @row_goal BIGINT = 9223372036854775807;

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (@row_goal) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
    LEFT OUTER JOIN dbo.Many(10) AS m ON
        1=1
) th ON p.ProductID = th.ProductID
OPTION (OPTIMIZE FOR (@row_goal = 1000000));

Tahminin 1000000 satır olduğunu görebiliriz:

1 M sıra

Bunların genellikle sorgu optimizasyonu için gerekli olmayan gelişmiş teknikler olduğuna dikkat etmeliyim. Daha fazla bilgi edinmek isterseniz Adam Machanic tarafından sunulan Clash of the Row Goals'ı izlemenizi tavsiye ederim .


dbo.Many işlevi

-- By Adam Machanic, reproduced with permission
IF EXISTS (SELECT * FROM sys.objects WHERE name = 'Many' AND OBJECT_SCHEMA_NAME(object_id) = 'dbo')
    DROP FUNCTION dbo.Many
GO
CREATE FUNCTION dbo.Many(@n INT)
RETURNS TABLE AS
RETURN
(
    WITH
    a(x) AS
    (
        SELECT
            *
        FROM
        (
            VALUES
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)
        ) AS x0(x)
    )
    SELECT TOP(@n)
        1 AS x
    FROM
        a AS a1,
        a AS a2
    WHERE
        a1.x % 2 = 0
)
GO

9

Doğrudan optimize ediciye bir kardinalite tahmini enjekte etmenin bir yolu yoktur, ancak ne elde etmek istediğinize bağlı olarak birkaç seçenek vardır.

OPTION (FAST N)Satır hedeflerini tanıtmak için bir sorgu ipucu kullanabilir ve muhtemelen enjekte etmek için CTE'leri veya alt sorguları kullanarak sorgunuzu yeniden yazabilirsinizTOP...ORDER BY yürütme planınızın farklı bölümlerine tabanlı satır hedeflerini , ancak başladığınızda sorgunuzun ne kadar verimli olacağından emin değilim daha karmaşık yapılar ile oynamak.

Optimize Edici'nin İçine Bakınız : Satır Hedefleri DerinlemesineDaha ayrıntılı bir açıklama için .

Optimize edicinin aldığı operatörleri etkilemek istiyorsanız, kardinalite tahminlerini enjekte etmeye gerek yoktur, ancak örneğin fiziksel birleştirme operatörlerini zorlamak için OPTION (MERGE JOIN)veya gibi şeyler kullanabilirsiniz OPTION (HASH JOIN).

Bu makale, ipuçlarını kullanarak bir planı nasıl etkileyeceğiniz hakkında daha ayrıntılı olarak açıklanmaktadır: İpuçlarıyla Yürütme Planlarını Denetleme

Bir planı düzeltmek istiyorsanız, bir plan kılavuzu da kullanabilirsiniz.

Yine, gerçek kullanım durumunuzun ne olduğu açık değildir ve bu tekniklerle kilometreniz değişebilir. Çoğu durumda, optimize edicinin karar vermesine izin vermek ve optimize edicinin bilinçli bir karar verebilmesi için güncel istatistiklere sahip olduğunuzdan emin olmak daha iyidir.


İlgili Microsoft Connect önerisi: Sorgularda xor88 ile filtre seçicilik ipucu belirtmeye izin ver . Microsoft şu yanıtı verdi:

Geri dönüşünüz için teşekkür ederiz. Bunun potansiyel faydasını görebiliyorum. Genel olarak otomatik davranışımızı olabildiğince iyi hale getirmeye çalışıyoruz ve bu tür ipuçlarına ihtiyaç duymuyoruz, ancak elbette başka birçok ipucumuz var. Bunu gelecekteki bir sürüm için düşüneceğiz, ancak Denali (11.0) sürümünün ötesinde olacak.

Saygılarımla,
Eric Hanson
Program Yöneticisi
SQL Server Sorgu İşleme


3

OPTIMIZE FORDerleme sırasında gerçek değeri (parametreleri) veya bilinmeyen değeri (değişkenleri) kullanmak yerine ipucu değerlerine dayalı kardinalite tahminini zorlamak için SQL Server sorgu ipucunu kullanabilirsiniz. Tüm ayrıntılar için SQL Server belgelerindeki Sorgu İpuçları konusuna bakın.

Örneğin, aşağıdaki sorgu, yerel değişkenlerle olduğu gibi, genel ortalama kardinalite yerine ipuçlu değerlerden istatistik histogramına göre satır sayılarını tahmin edecektir.

DECLARE 
      @StartDate datetime = '20150101'
    , @EndDate datetime = '20150102';
SELECT *
FROM dbo.Example
WHERE
    DateColumn BETWEEN  @StartDate AND @EndDate
OPTION(OPTIMIZE FOR(@StartDate = '20100101', @EndDate='20100101'));

Benzer şekilde, ipucu parametreler için kullanılabilir, böylece tahminler derleme sırasında gerçek parametre değerleri yerine ipucu değerlerinden alınan istatistik histogramına dayanır.

DECLARE 
      @StartDate datetime = '20150101'
    , @EndDate datetime = '20150102';
EXECUTE sp_executesql N'SELECT *
        FROM dbo.Example
        WHERE
            DateColumn BETWEEN  @StartDate AND @EndDate
        OPTION(OPTIMIZE FOR(@StartDate = ''20100101'', @EndDate=''20100101''));'
    , N'@StartDate datetime, @EndDate datetime'
    , @StartDate = @StartDate
    , @EndDate = @EndDate;

UNKNOWNAnahtar yerine, gerçek parametre değeri ve istatistikleri histogram göre tahmin edilmesi genel ortalama önem düzeyi kullanımı ipucu yerine bir sabit bölgesinin belirtilebilir.

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.