Suggerimento per la cardinalità di SQL Server


14

Esiste un modo per "iniettare" una stima della cardinalità in un ottimizzatore di SQL Server (qualsiasi versione)?

cioè qualcosa di simile al suggerimento di cardinalità di Oracle.

La mia motivazione è guidata dall'articolo, Quanto sono buoni gli ottimizzatori di query, davvero? [1] , in cui testano l'influenza dello stimatore della cardinalità su una selezione di un piano negativo. Pertanto, sarebbe sufficiente se potessi forzare SQL Server a "stimare" le cardinalità proprio per query complesse.


[1] Leis, Viktor, et al. "Quanto sono bravi gli ottimizzatori di query, davvero?"
Atti del VLDB Endowment 9.3 (2015): 204-215.

Risposte:


10

Puoi ottenere qualcosa di simile al CARDINALITYsuggerimento di Oracle usando strategicamente TOPe una funzione definita dall'utente chiamata MANY() sviluppata da Adam Machanic . Analizziamo alcuni esempi. Sto usando il database AdventureWorks disponibile gratuitamente. Supponiamo che abbia davvero bisogno di controllare il numero di righe restituite dalla thtabella derivata nella seguente query:

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;

Come è, ottengo una stima di 113443 righe:

query iniziale

Se devo abbassare il preventivo, thposso utilizzarlo TOPinsieme al OPTIMIZE FORsuggerimento per impostare un obiettivo di riga. Ecco un modo per farlo:

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));

Possiamo vedere che il preventivo è solo 1 riga:

Stima a 1 riga

Ho impostato @row_goalil BIGINTvalore più grande possibile per evitare di modificare i risultati. Il OPTIMIZE FORsuggerimento per la query indica all'ottimizzatore di ottimizzare la query come se@row_goal fosse uguale a 1. Otterrò gli stessi risultati ma la query verrà ottimizzata in modo diverso.

Aumentare una stima della cardinalità è più complicato. Non possiamo semplicemente aumentare il valore per TOPperché l'ottimizzatore si renderà conto che non restituirà abbastanza righe. Tuttavia, possiamo usare la MANY()funzione per aggiungere righe al preventivo. Si noti che la MANY()funzione restituirà sempre 0 righe, ma la stima delle righe da essa cambia con il parametro di input. Supponiamo che sia necessario aumentare di 10 volte la stima delle righe dalla tabella derivata. Un modo per ottenere ciò:

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;

Possiamo vedere che la stima è 10 volte la tabella di base:

Query 10X

Il superfluo è TOPstato aggiunto per impedire all'ottimizzatore di spostare le tabelle. Senza di essa ilMANY() funzione può essere applicata nel punto sbagliato del piano.

È possibile combinare le due tecniche se si desidera una sopravvalutazione precisa anziché semplicemente moltiplicare il numero di righe per un fattore. Ad esempio, supponiamo che tu abbia davvero bisogno che la stima della tabella derivata sia esattamente 1000000 righe. Un modo per ottenere ciò:

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));

Possiamo vedere che la stima è di 1000000 righe:

1 M file

Devo avvertirti che si tratta di tecniche avanzate che spesso non sono necessarie per l'ottimizzazione delle query. Se vuoi saperne di più, ti consiglio di guardare gli obiettivi di Clash of the Row presentati da Adam Machanic.


dbo.Molti funzione

-- 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

Non è possibile iniettare una stima della cardinalità direttamente nell'ottimizzatore, ma a seconda di ciò che si desidera ottenere ci sono alcune opzioni.

È possibile utilizzare un OPTION (FAST N)suggerimento per la query per introdurre obiettivi di riga e possibilmente riscrivere la query utilizzando CTE o subquery per iniettareTOP...ORDER BY obiettivi di riga basati su diverse parti del piano di esecuzione, ma non sono sicuro dell'efficacia della query risultante all'avvio giocare con le costruzioni più complesse.

Consulta la sezione Inside the Optimizer: Row Goals In Depth per una spiegazione più approfondita.

Se si desidera influenzare gli operatori scelti dall'ottimizzatore, non è necessario provare a iniettare stime di cardinalità ma è possibile utilizzare cose come OPTION (MERGE JOIN)oOPTION (HASH JOIN) ad esempio, per forzare gli operatori di join fisici.

Questo articolo fornisce ulteriori dettagli su come influenzare un piano usando i suggerimenti: Controllo dei piani di esecuzione con suggerimenti

Se vuoi riparare un piano puoi anche usare una guida di piano.

Ancora una volta non è chiaro quale sia il tuo caso d'uso reale e il tuo chilometraggio può variare usando queste tecniche. In molti casi è meglio lasciare che l'ottimizzatore decida e assicurarsi di disporre di statistiche aggiornate in modo che l'ottimizzatore possa prendere una decisione informata.


Suggerimento Microsoft Connect pertinente: consentire di specificare il suggerimento di selettività del filtro nelle query di xor88. Microsoft ha risposto:

Grazie per il feedback. Posso vedere il potenziale beneficio di questo. In generale, ci impegniamo a fondo per rendere il nostro comportamento automatico il migliore possibile ed evitare la necessità di questo tipo di suggerimento, ma ovviamente abbiamo molti altri suggerimenti. Lo considereremo per una versione futura, ma sarebbe oltre la versione Denali (11.0).

Cordiali saluti,
Eric Hanson
Program Manager
SQL Server Query Processing


3

È possibile utilizzare il OPTIMIZE FORsuggerimento per le query di SQL Server per forzare la stima della cardinalità basata su valori suggeriti anziché utilizzare il valore effettivo (parametri) o il valore sconosciuto (variabili) durante la compilazione. Vedere l' argomento Suggerimenti per le query nella documentazione di SQL Server per i dettagli completi.

Ad esempio, la query seguente stimerà il conteggio delle righe in base all'istogramma delle statistiche in base ai valori suggeriti anziché alla cardinalità media complessiva, come farebbe altrimenti con le variabili locali.

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

Allo stesso modo, il suggerimento può essere utilizzato per i parametri in modo che le stime siano basate sull'istogramma delle statistiche dai valori suggeriti anziché dai valori dei parametri effettivi durante la compilazione.

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;

La UNKNOWNparola chiave può essere specificata anziché un valore letterale nel suggerimento per utilizzare la cardinalità media complessiva invece di stimare in base al valore del parametro effettivo e all'istogramma delle statistiche.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.