Domanda

Ho colpito un intoppo interessante (interessante per me, almeno). Qui di seguito è un'idea generale di ciò che la mia domanda assomiglia. Si supponga @AuthorType è un input per la stored procedure e che ci sono varie condizioni specializzate ogni posto ho messo i commenti.

SELECT *
FROM TBooks
WHERE
(--...SOME CONDITIONS)
OR
(@AuthorType = 1 AND --...DIFFERENT CONDITIONS)
OR
(@AuthorType = 2 AND --...STILL MORE CONDITIONS)

La cosa interessante per me è che se eseguo questo SP con @AuthorType = 0, che gira più lento di se rimuovere le ultime due serie di condizioni (quelli che aggiungono le condizioni per i valori specializzate di @AuthorType).

Nel caso non SQL Server realizzare in fase di esecuzione che tali condizioni non saranno mai conseguiti e li ignorano del tutto? La differenza che sto vivendo non è piccolo; è all'incirca raddoppiare la lunghezza della query (1-2 secondi a 3-5 secondi).

mi aspetto di SQL Server per ottimizzare questo troppo per me? Ho davvero bisogno di avere 3 SP separate per le condizioni specializzate?

È stato utile?

Soluzione

  

Nel caso non SQL Server realizzare al   runtime che tali condizioni   mai incontrato e ignorarli del tutto?

No, assolutamente no. Ci sono due fattori in gioco qui.

  1. SQL Server fa non garanzia operatore booleano corto circuito. Vedere su SQL Server operatore booleano cortocircuito per un esempio che mostra chiaramente come l'ottimizzazione delle query in grado di invertire l'ordine di valutazione di espressione booleana. Mentre a una prima impressione questo sembra come un insetto alla C imperativo come insieme di mente di programmazione, è la cosa giusta da fare per il set dichiarativa mondo di SQL oriented.

  2. O è il nemico di SQL SARGability. istruzioni SQL sono compliled in un piano di esecuzione, il piano viene eseguito. Il piano viene riutilizzato tra invocazioni (memorizzato nella cache). Come tale il compilatore SQL deve generare un unico piano che si adatta tutti i casi distinti OR (@ AuthorType = 1 @ AuthorType = 2 @ AuthorType = 3). Quando si tratta di generare il piano di query è esattamente come se @AuthorType avrebbe tutti i valori in una volta, in un senso. Il risultato è quasi sempre la peggiore piano possibile, che non può beneficiare qualsiasi indice quanto vari rami OR contrariarsi così finisce la scansione dell'intero tavolo e controllando righe una ad una.

Il bestthing di fare nel tuo caso, e ogni altro caso che coinvolge booleana OR, è quello di spostare il @AuthorType al di fuori della query:

IF (@AuthorType = 1)
  SELECT ... FROM ... WHERE ...
ELSE IF (@AuthorType = 2)
  SELECT ... FROM ... WHERE ...
ELSE ...

Poiché ogni ramo è chiaramente separato nella propria dichiarazione, SQL può creare il percorso di accesso appropriato per ogni singolo caso.

La cosa migliore da fare è quella di utilizzare UNION ALL, il modo in cui chadhoc già suggerito, ed è il giusto approccio in vista o in altri luoghi in cui è necessario un unico prospetto (No se è consentito).

Altri suggerimenti

Ha a causa di quanto sia difficile per l'ottimizzatore di gestire "OR" logico tipo insieme problemi a che fare con noreferrer parametro sniffing . Provare a cambiare la query di cui sopra per la politica dell'Unione, come accennato nel post qui . cioè si finirà con più istruzioni unioned insieme con un solo @AuthorType = x E, consentendo l'ottimizzatore di escludere porzioni dove e logica non corrisponde alla data @AuthorType, e cercare nelle indici appropriati, a sua volta .. . sarebbe simile a questa:

SELECT *
FROM TBooks
WHERE
(--...SOME CONDITIONS)
AND @AuthorType = 1 AND --...DIFFERENT CONDITIONS)
union all
SELECT *
FROM TBooks
WHERE
(--...SOME CONDITIONS)
AND @AuthorType = 2 AND --...DIFFERENT CONDITIONS)
union all
...
  

dovrei combattere la voglia di ridurre le duplicazioni ... ma l'uomo, che in realtà non si sente bene a me.

Sarebbe questo "sentire" meglio?

SELECT ... lots of columns and complicated stuff ...
FROM 
(
    SELECT MyPK
    FROM TBooks
    WHERE 
    (--...SOME CONDITIONS) 
    AND @AuthorType = 1 AND --...DIFFERENT CONDITIONS) 
    union all 
    SELECT MyPK
    FROM TBooks
    WHERE 
    (--...SOME CONDITIONS) 
    AND @AuthorType = 2 AND --...DIFFERENT CONDITIONS) 
    union all 
    ... 
) AS B1
JOIN TBooks AS B2
    ON B2.MyPK = B1.MyPK
JOIN ... other tables ...

La tabella B1 pseudo è solo la clausola WHERE per ottenere i PK. Che è poi unito di nuovo alla tabella originale (e tutti gli altri che sono necessari) per ottenere la "presentazione". Questo evita la duplicazione delle colonne di presentazione in ogni UNION ALL

Si può prendere questo un ulteriore passo avanti e inserire PK in una tabella temporanea prima, e poi unire che per le altre tabelle per l'aspetto presentazione.

Lo facciamo per tabelle di grandi dimensioni in cui l'utente ha un sacco di scelte su cosa interrogare su.

DECLARE @MyTempTable TABLE
(
    MyPK int NOT NULL,
    PRIMARY KEY
    (
        MyPK
    )
)

IF @LastName IS NOT NULL
BEGIN
   INSERT INTO @MyTempTable
   (
        MyPK
   )
   SELECT MyPK
   FROM MyNamesTable
   WHERE LastName = @LastName -- Lets say we have an efficient index for this
END
ELSE
IF @Country IS NOT NULL
BEGIN
   INSERT INTO @MyTempTable
   (
        MyPK
   )
   SELECT MyPK
   FROM MyNamesTable
   WHERE Country = @Country -- Got an index on this one too
END

... etc

SELECT ... presentation columns
FROM @MyTempTable AS T
    JOIN MyNamesTable AS N
        ON N.MyPK = T.MyPK -- a PK join, V. efficient
    JOIN ... other tables ...
        ON ....
WHERE     (@LastName IS NULL OR Lastname @LastName)
      AND (@Country IS NULL OR Country @Country)

Si noti che tutti i test sono ripetuti [tecnicamente si don; t bisogno il @Lastname uno :)]., Compresi quelli che erano oscure (diciamo) non nei filtri originali per creare @MyTempTable

La creazione di @MyTempTable è stato progettato per sfruttare al meglio tutto ciò che sono disponibili i parametri. Forse, se entrambi @LastName E @Country sono disponibili che è di gran lunga più efficiente a riempire il tavolo di uno di loro, così creiamo un caso per questo scenario.

problemi di scala? Rivedere quali query reali sono stati fatti e aggiungere casi per quelli che possono essere migliorate.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top