2012-06-20 4 views
8

Ho una procedura memorizzata che funziona terribilmente. Quando dichiaro una variabile, imposta il suo valore e quindi la utilizzo nella clausola where, l'istruzione impiega più di un'ora per essere eseguita. Quando faccio un duro codice delle variabili nella clausola where viene eseguito in meno di un secondo.Il parametro non esegue altrettanto bene la codifica hard del valore

Ho iniziato a esaminare cosa c'era di sbagliato con i piani di esecuzione. Sembra che quando provo a passarle alcune variabili dichiarate, il piano di esecuzione esegue il crashare di una corrispondenza hash perché seleziona i valori da una vista che utilizza UNION e un'espressione di tabella comune.

 
/************* Begin of Stored Procedure ***************/ 
CREATE PROCEDURE GetFruit 
    @ColorId bigint, 
    @SeasionId bigint 
WITH RECOMPILE 
AS 
BEGIN 

SELECT 
    A.Name 
FROM 
    [Apple_View] A /* This is the view down below */ 
    INNER JOIN [Fruit] F 
     ON (F.ColorId = @ColorId 
      AND A.FruitId = F.FruitId)   
WHERE 
    (A.ColorId = @ColorId 
    AND 
    A.SeasonId = @SeasonId) 

END 
/************* End of Stored Procedure ***************/ 

/************* Begin of View ***************/ 
WITH Fruits (FruitId, ColorId, SeasonId) AS 
(
    -- Anchor member 
    SELECT 
     F.FruitId 
     ,F.ColorId 
     ,F.SeasonId 
    FROM 
     (( 
      SELECT DISTINCT 
       EF.FruitId 
       ,EF.ColorId 
       ,EF.SeasonId 
       ,EF.ParentFruitId 
      FROM 
       ExoticFruit EF 
       INNER JOIN Fruit FR 
        ON FR.FruitId = EF.FruitId 
     UNION 
      SELECT DISTINCT 
       SF.FruitId 
       ,SF.ColorId 
       ,SF.SeasonId 
       ,SF.ParentFruitId    
      FROM 
       StinkyFruit SF 
       INNER JOIN Fruit FR 
        ON FR.FruitId = SF.FruitId 
     UNION 
      SELECT DISTINCT 
       CF.FruitId 
       ,CF.ColorId 
       ,CF.SeasonId 
       ,CF.ParentFruitId 
      FROM 
       CrazyFruit CF 
       INNER JOIN Fruit FR 
        ON FR.FruitId = CF.FruitId 

      )) f 

    UNION ALL 

    -- Recursive Parent Fruit 
    SELECT 
     FS.FruitId 
     ,FS.ColorId 
     ,FS.SeasonId 
     ,FS.ParentFruitId 
    FROM 
     Fruits FS 
     INNER JOIN MasterFruit MF 
      ON MF.[ParentFruitId] = fs.[FruitId] 
) 

SELECT DISTINCT 
    FS.FruitId 
    ,FS.ColorId 
    ,FS.SeasonId 
    FROM 
     Fruits FS 

/************* End of View ***************/ 


/* To Execute */ 
EXEC GetFruit 1,3 

Se eseguire la stored procedure utilizzando i valori impostati ci vuole più di un'ora e qui è il piano di esecuzione. With Variables

Se eseguire la stored procedure di rimuovere i valori di dichiarare e SET e basta impostare la clausola Dove la seguente dichiarazione viene eseguito in meno di un secondo e qui è il piano di esecuzione:

WHERE(A.ColorId = 1 AND A.SeasonId = 3) 

hard coded where clause

Si noti come le variabili codificate hard utilizzano l'indicizzazione mentre il primo utilizza un hash. Perché? Perché i valori hard coded nella clausola where funzionano in modo diverso rispetto alle variabili dichiarate?

------- questo è quello che finalmente effettuato con l'ausilio di @ user1166147 ------

ho cambiato la stored procedure da utilizzare sp_executesql.

 
CREATE PROCEDURE GetFruit 
    @ColorId bigint, 
    @SeasionId bigint 
WITH RECOMPILE 
AS 
BEGIN 

DECLARE @SelectString nvarchar(max) 

SET @SelectString = N'SELECT 
    A.Name 
FROM 
    [Apple_View] A /* This is the view down below */ 
    INNER JOIN [Fruit] F 
     ON (F.ColorId = @ColorId 
      AND A.FruitId = F.FruitId)   
WHERE 
    (A.ColorId = ' + CONVERT(NVARCHAR(MAX), @ColorId) + ' 
    AND 
    A.SeasonId = ' + CONVERT(NVARCHAR(MAX), @SeasonId) + ')' 

EXEC sp_executesql @SelectString 

END 
+0

Avete controllato per assicurarsi che i tipi di dati dei parametri corrispondono ai tipi di dati di colonna? – HackedByChinese

+2

Queste sono variabili non parametri. SQL Server non esegue lo sniffing delle variabili, pertanto le stime di selettività verranno prese in considerazione. Cosa succede se si aggiunge 'OPTION (RECOMPILE)'? –

+1

utilizza l'opzione ricompilare. C'è qualcosa chiamato sniffing dei parametri in cui SQL Server genera un piano di query diverso a seconda dei valori di input – praveen

risposta

2

Modifica riepilogo Per una richiesta da Damien_The_Unbeliever

L'obiettivo è quello di ottenere la migliore/più informazioni circa il valore della variabile a SQL prima della creazione del piano, in genere il parametro sniffing fa questo. Potrebbe esserci un motivo per cui il parametro sniffing è stato "disabilitato" in questo caso. Senza vedere una rappresentazione migliore del codice reale non possiamo veramente dire quale sia la soluzione o perché il problema esista. Prova le cose qui sotto per forzare le aree interessate a generare piani usando valori reali.

* versione lunga con maggiori dettagli *

È questo il tuo proc effettivo memorizzato? Hai valori predefiniti per i tuoi parametri? Se sì, quali sono?

Lo sniffing dei parametri può essere d'aiuto - ma deve avere valori di parametri tipici per creare bene il piano e, in caso contrario, non aiuterà o creerà un piano errato in base al valore non tipico del parametro. Quindi se una variabile ha un valore predefinito di null o un valore che non è un valore tipico la prima volta che viene eseguito e il piano compilato, crea un piano errato.

Se qualcun altro ha scritto questo sproc, potrebbero avere intenzionalmente "disabilitato" il parametro sniffing con le variabili locali per un motivo. Le regole aziendali possono richiedere queste strutture variabili.

L'obiettivo è ottenere la migliore/la maggior parte delle informazioni sul valore della variabile su SQL PRIMA che il piano sia stato creato, e generalmente Parameter Sniffing lo fa. Ma ci sono cose che possono influire negativamente sulle prestazioni, e questo potrebbe essere il motivo per cui è "disabilitato".Sembra ancora che il piano sia stato creato con valori atipici per i parametri o non ancora abbastanza informazioni - usando il parametro sniffing o no.

Provare a chiamare la query all'interno di sproc con Usa sp_executesql per eseguire le query interessate, forzandolo a generare un piano per quell'area con le variabili effettive e vedere se è meglio. Questa può essere la soluzione se devi avere questo tipo di valore di parametro irregolare - creare processi memorizzati che eseguono le parti interessate e chiamarle in un secondo momento all'interno della stored procedure - dopo che la variabile ha ricevuto un valore tipico.

Senza vedere una rappresentazione migliore del codice effettivo, è difficile capire quale sia il problema. Speriamo che queste informazioni possano aiutarti -

+0

Mentre sei in uno stato d'animo di montaggio - al momento, questo è solo un muro di testo. Forse prova a dividerlo in paragrafi? –

+0

Non è la procedura memorizzata effettiva. È preciso al 95%, ha solo modifiche ai nomi delle colonne per mantenere privati ​​i nomi delle tabelle e delle colonne dal suo sistema finanziario. * So che fa schifo e rende più difficile. Mi scuso per averlo fatto, ma ho fatto del mio meglio per renderlo il più vicino possibile con modifiche minime – Mark

+0

Sono riuscito a farlo funzionare in meno di un secondo usando "sp_executesql". Metterò la soluzione nella mia domanda per i futuri utenti. – Mark

0

Puoi forzarlo a ottimizzare la query in base ai valori tipici che potresti conoscere meglio. Per la query originale aggiungere il seguente:

OPTION (OPTIMIZE FOR(@ColorId = 1, @SeasionId = 3)) 

Avrebbe effetto simile senza SQL dinamico. Se non si conoscono i valori tipici, è possibile effettuare ottimizzatore li sniffing:

OPTION (OPTIMIZE FOR UNKNOWN) 

Anche in questo caso, non SQL dinamico

Problemi correlati