2010-03-05 9 views
6

Ho una tabella itemValue pieno di dati su un server SQL 2005 in esecuzione in modalità di compatibilità 2000 sembra qualcosa di simile (si tratta di una tabella di valori definiti dall'utente):Ottimizzazione SQL - il piano di esecuzione cambia in base al valore del vincolo - Perché?

ID ItemCode  FieldID Value 
-- ---------- ------- ------ 
1 abc123    1 D 
2 abc123    2 287.23 
4 xyz789    1 A 
5 xyz789    2 3782.23 
6 xyz789    3 23 
7 mno456    1 W 
9 mno456    3 45 
           ... and so on. 

fieldid viene da il ItemField tavolo:

ID FieldNumber DataFormatID Description ... 
-- ----------- ------------ ----------- 
1    1    1 Weight class 
2    2    4 Cost 
3    3    3 Another made up description 
.    .    x xxx 
.    .    x xxx 
.    .    x xxx 
x    91 (we have 91 user-defined fields) 

Perché non posso PIVOT in modalità 2000, siamo bloccati costruire una query di brutto utilizzando casi e GROUP BY per ottenere i dati di guardare come dovrebbe per s ome applicazioni legacy, che è:

ItemNumber Field1 Field2 Field3 .... Field51 
---------- ------ ------- ------ 
    abc123 D  287.23 NULL 
    xyz789 A  3782.23 23 
    mno456 W  NULL  45 

potete vedere abbiamo solo bisogno di questa tabella per mostrare valori fino a 51 ° UDF. Ecco la domanda:

SELECT 
    iv.ItemNumber, 
    ,MAX(CASE WHEN f.FieldNumber = 1 THEN iv.[Value] ELSE NULL END) [Field1] 
    ,MAX(CASE WHEN f.FieldNumber = 2 THEN iv.[Value] ELSE NULL END) [Field2] 
    ,MAX(CASE WHEN f.FieldNumber = 3 THEN iv.[Value] ELSE NULL END) [Field3] 
     ... 
    ,MAX(CASE WHEN f.FieldNumber = 51 THEN iv.[Value] ELSE NULL END) [Field51] 
FROM ItemField f 
LEFT JOIN ItemValue iv ON f.ID = iv.FieldID 
WHERE f.FieldNumber <= 51 
GROUP BY iv.ItemNumber 

Quando il vincolo FieldNumber è < = 51, l'esecuzione piano va qualcosa come:

SELECT <== Computer Scalar <== Stream Aggregate <== Sort (Cost: 70%) <== Hash Match <== (Clustered Index Seek && Table Scan)

ed è veloce! Posso ritirare oltre 100.000 dischi in circa un secondo, che si adatta alle nostre esigenze.

Tuttavia, se avessimo più UDF e modificare il vincolo a qualsiasi cosa sopra (sì, ho provato uno per uno) o se rimuovere completamente, perdo il Sort nel piano di esecuzione, ed è viene sostituito con un intero gruppo di blocchi di parallelismo che raccolgono, ripartizionano e distribuiscono flussi, e l'intera cosa è lenta (30 secondi anche per solo 1 record).

FieldNumber ha un cluster, indice univoco, ed è parte della chiave primaria composta con la ID colonna (indice non cluster) nella tavolo ItemField. Il itemValue della tabella ID e ItemNumber colonne fanno una PK, e v'è un ulteriore indice non cluster sulla colonna diItemNumber.

Qual è il ragionamento alla base di questo? Perché la modifica del mio semplice vincolo di intero modifica l'intero piano di esecuzione?

E se sei all'altezza ... cosa faresti diversamente? C'è un aggiornamento SQL pianificato per un paio di mesi, ma ho bisogno di risolvere questo problema prima.

+0

Quello che farei diversamente non è usare quella struttura di tabella. È molto meglio utilizzare una struttura con campi definiti per il 99% delle esigenze di dati che possono essere individuate in anticipo rispetto all'utilizzo di una tabella EAV. Per inciso, la modalità compatilbity non ti impedisce di utilizzare nuove funzionalità, è di consentire l'utilizzo di funzionalità che non sono più consentite. Tuttavia, se si sta sviluppando su un database 2005 con un database di produzione 2000, è meglio evitare le nuove funzionalità. – HLGEM

risposta

4

SQL Server è abbastanza intelligente da prendere in considerazione i vincoli CHECK durante l'ottimizzazione delle query.

Il f.FieldNumber <= 51 viene ottimizzato e l'ottimizzatore rileva che è necessario unire tutte e due le tabelle (operazione migliore con un HASH JOIN).

Se non si dispone del vincolo, è necessario che il motore verifichi la condizione e molto probabilmente utilizzi l'attraversamento dell'indice per eseguire questa operazione. Questo potrebbe essere più lento.

Potrebbe per piacere postare l'intero piano per le domande? Basta eseguire SET SHOWPLAN_TEXT ON e poi le domande.

Aggiornamento:

Qual è il ragionamento dietro questo? Perché la modifica del mio semplice vincolo di intero modifica l'intero piano di esecuzione?

Se per limitazione si intende la condizione WHERE, questa è probabilmente l'altra cosa.

Impostare le operazioni (è ciò che fa SQL) non dispone di un singolo algoritmo più efficiente: l'efficienza di ciascun algoritmo dipende fortemente dalla distribuzione dei dati nei set.

Supponiamo, per l'utilizzo di un sottoinsieme (è ciò che fa la clausola WHERE) è possibile trovare l'intervallo di record nell'indice e utilizzare i puntatori del record dell'indice per individuare le righe di dati nella tabella, o semplicemente eseguire la scansione di tutti i record in la tabella e filtrarli utilizzando la condizione WHERE.

Efficienza della prima operazione è m × const, quella di quest'ultimo è n, dove m è il numero di record che soddisfa la condizione, n è il numero totale di record nella tabella e const > 1.

Ciò significa che per valori maggiori di m fullscan è più efficiente.

SQL Server ne è a conoscenza e modifica i piani di esecuzione in base alle costanti che influiscono sulla distribuzione dei dati nelle operazioni impostate.

per fare questo, SQL Server mantiene statistiche: istogrammi aggregati della distribuzione dei dati in ogni colonna indicizzata e li utilizza per costruire i piani di query.

Pertanto, la modifica del numero intero nella condizione WHERE influisce sulle dimensioni e sulla distribuzione dei dati degli insiemi sottostanti e rende SQL Server in grado di riconsiderare gli algoritmi più adatti per lavorare con i set di tale dimensione e layout.

+0

Non vedo alcuna menzione di un vincolo CHECK in OP ... –

+0

Pubblicherò i piani, ma devo fare un paio di modifiche in modo che le mie vere tabelle non forniscano alcuna informazione identificativa (il mio esempio è truccato). –

+0

@Remus: Quando l'ho letto ho creduto che * quando il vincolo FieldNumber fosse '<= 51' * riguardava un vincolo' CHECK', ma probabilmente hai ragione, il @op molto ha significato la condizione 'WHERE'. – Quassnoi

0

esso viene sostituito con un gruppo intero di blocchi di parallelismo

Prova questa:

SELECT 
    iv.ItemNumber, 
    ,MAX(CASE WHEN f.FieldNumber = 1 THEN iv.[Value] ELSE NULL END) [Field1] 
    ,MAX(CASE WHEN f.FieldNumber = 2 THEN iv.[Value] ELSE NULL END) [Field2] 
    ,MAX(CASE WHEN f.FieldNumber = 3 THEN iv.[Value] ELSE NULL END) [Field3] 
     ... 
    ,MAX(CASE WHEN f.FieldNumber = 51 THEN iv.[Value] ELSE NULL END) [Field51] 
FROM ItemField f 
LEFT JOIN ItemValue iv ON f.ID = iv.FieldID 
WHERE f.FieldNumber <= 51 
GROUP BY iv.ItemNumber 
OPTION (Maxdop 1) 

Utilizzando OPTION (MAXDOP 1), questo dovrebbe evitare che la parellelism nel piano di esecuzione.

+0

Quindi funziona davvero, ma non è possibile inserire OPTION in una vista in quanto ne ho bisogno. –

0

A 66 si sta colpendo una soglia di stima dei costi interni che decide che è meglio utilizzare un piano rispetto all'altro. Che cos'è questa soglia e perché accade non è davvero importante. Si noti che la query è diversa con ciascun valore FieldNumber, poiché non si modifica solo il WHERE: si modificano anche i campi proiettati pseudo-'pivot '.

Ora io non conosco tutti i dettagli della vostra tavola e le vostre domande e inserisco modello/update/delete /, ma per la query particolare che hai postato il corretto struttura di indice cluster per la tabella itemValue è questo:

CREATE CLUSTERED INDEX [cdxItemValue] ON ItemValue (FieldID, ItemNumber); 

Questa struttura elimina la necessità di ordinare in modo intermedio i risultati per questa query 'pivot'.