2010-11-19 11 views
11

Ho una query ricorsiva che viene eseguita molto velocemente se la clausola WHERE contiene una costante ma diventa molto lenta se sostituisco la costante con un parametro con lo stesso valore.Perché una query rallenta drasticamente se nella clausola WHERE una costante viene sostituita da un parametro (con lo stesso valore)?

Domanda # 1 - con la costante

;WITH Hierarchy (Id, ParentId, Data, Depth) 
AS 
(SELECT Id, ParentId, NULL AS Data, 0 AS Depth 
    FROM Test 
    UNION ALL 
    SELECT h.Id, t.ParentId, COALESCE(h.Data, t.Data), Depth + 1 AS Depth 
    FROM Hierarchy h 
     INNER JOIN Test t ON t.Id = h.ParentId 
) 
SELECT * 
FROM Hierarchy 
WHERE Id = 69 

Query # 2 - con il parametro

DECLARE @Id INT 
SELECT @Id = 69 

;WITH Hierarchy (Id, ParentId, Data, Depth) 
AS 
(SELECT Id, ParentId, NULL AS Data, 0 AS Depth 
    FROM Test 
    UNION ALL 
    SELECT h.Id, t.ParentId, COALESCE(h.Data, t.Data), Depth + 1 AS Depth 
    FROM Hierarchy h 
     INNER JOIN Test t ON t.Id = h.ParentId 
) 
SELECT * 
FROM Hierarchy 
WHERE Id = @Id 

In caso di una tabella con 50.000 righe la query con le piste costanti per 10 millisecondi e quello con il parametro funziona per 30 secondi (3000 volte più lento).

Non è un'opzione per spostare l'ultima clausola WHERE alla definizione di ancoraggio della ricorsione, poiché desidero utilizzare la query per creare una vista (senza l'ultimo WHERE). La selezione dalla vista avrebbe la clausola WHERE (WHERE Id = @Id) - Ne ho bisogno a causa di Entity Framework, ma questa è un'altra storia.

Qualcuno può suggerire un modo per forzare la query n. 2 (con il parametro) per utilizzare lo stesso piano di query della query n. 1 (con la costante)?

Ho già provato a giocare con gli indici, ma questo non ha aiutato.

Se qualcuno vorrebbe poter pubblicare la definizione della tabella e alcuni dati di esempio. Sto usando SQL 2008 R2.

Grazie per il vostro aiuto in anticipo!

esecuzione del piano - Domanda # 1 - con la costante

alt text

esecuzione del piano - Query # 2 - con il parametro

alt text

+4

Piani di esecuzione post. – Donnie

+0

Si prega di controllare i piani di esecuzione appena aggiunti. – Zoltan

+3

Sembra che il buon piano spinga il predicato molto più in basso. Simile al problema discusso nella seconda parte di questo articolo http://jahaines.blogspot.com/2010/03/performance-tuning-101-what-you-will_17.html –

risposta

5

Come suggerito da Martin in un commento sotto la domanda, il problema è che il server SQL non esegue correttamente il push del predicato dalla clausola WHERE - vedere il collegamento nel suo commento.

Ho finito per creare una funzione con valori di tabella definita dall'utente e usarla con l'operatore CROSS APPLY per creare la vista.

Vediamo la soluzione stessa.

User Defined

CREATE VIEW [dbo].[TestView] 
AS 
SELECT t.Id, t.ParentId, f.Data, f.Depth 
FROM 
    Test AS t 
    CROSS APPLY TestFunction(Id) as f 

Query con valori di tabella funzioni

CREATE FUNCTION [dbo].[TestFunction] (@Id INT) 
RETURNS TABLE 
AS 
RETURN 
(
    WITH 
    Hierarchy (Id, ParentId, Data, Depth) 
    AS(
    SELECT Id, ParentId, NULL AS Data, 0 AS Depth FROM Test Where Id = @Id 
    UNION ALL 
    SELECT h.Id, t.ParentId, COALESCE(h.Data, t.Data), Depth + 1 AS Depth 
     FROM Hierarchy h 
      INNER JOIN Test t ON t.Id = h.ParentId 
    ) 
    SELECT * FROM Hierarchy 
) 

View con costante

SELECT * FROM TestView WHERE Id = 69 

query con il parametro

DECLARE @Id INT 
SELECT @Id = 69 
SELECT * FROM TestView WHERE Id = @Id 

La query con il parmater esegue fondamentalmente veloce come la query con la costante.

Grazie Martin e anche per gli altri!

1

Per la vostra seconda query provare a utilizzare OPTIMIZE FOR o OPTION (RECOMPILE) query hint per vedere se questo lo costringe a ricompilare lile in base al valore del parametro fornito.

+0

L'ho già provato e non ha funzionato. – Zoltan

+0

Se SQL spingesse correttamente verso il basso la clausola WHERE della query, questa sarebbe una soluzione come per es. @Id = 69 la query viene eseguita molto velocemente. Tuttavia, questo non è il caso. – Zoltan

1

È necessario utilizzare uno plan guide per bloccare il piano desiderato.

+0

Come può essere generato? È semplice prendere il piano con una costante e in qualche modo hackerare l'XML per incorporare il parametro? –

+0

Ho finito con la stessa conclusione di Martin, è possibile/facile prendere la guida di piano per la query con il letterale e applicarla per la query con il parametro? – Zoltan

1

Questo potrebbe essere il suggerimento peggiore di sempre, ma hai pensato di creare uno sproc per creare la tua query come stringa ed eseguirla usando sp_executesql?

Non so nulla del comportamento di caching di SQL eseguito da sp_executesql, è stata solo la prima cosa che mi è venuta in mente.

+0

O anche solo il comando EXEC. Sarei davvero interessato a sapere se mi è stato di aiuto. – David

+0

Il problema è che vorrei evitare procedure totalmente memorizzate. È necessario finire con una vista poiché è possibile scrivere una SELECT da una vista, ma ciò non è vero per una procedura memorizzata. Per quanto riguarda il tuo suggerimento, anche se non l'ho provato, per quanto comprendo il server SQL, non farebbe alcuna differenza in caso di mio problema. Il parametro sarebbe ancora lì nella query, quindi il piano di esecuzione rimarrebbe lo stesso. – Zoltan

+0

Hai perso il mio punto (probabilmente poco chiaro). È possibile utilizzare il parametro per scrivere una query senza parametri. Se il parametro @Id è uguale a 100 (per esempio), è possibile scrivere una stringa SQL "SELECT ..... WHERE @Id = '+ @Id ed eseguire la stringa SQL. – David

Problemi correlati