Campo calcolato SQL nella clausola SELECT e GROUP BY


11

Spesso durante l'interrogazione dei miei database MS SQL Server, devo creare un campo calcolato, come questo

(CASE WHEN A.type = 'Workover' THEN 'Workover' 
      ELSE (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' 
                 WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' 
                 WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' 
                 ELSE 'Other' 
            END)
END)

e quindi ho bisogno di raggruppare i miei risultati per questo campo calcolato (tra gli altri). Quindi, ho lo stesso calcolo in entrambe le clausole SELECT e GROUP BY. SQL Server sta effettivamente eseguendo questi calcoli due volte o è abbastanza intelligente da farlo solo una volta?

Risposte:


13

Ho lo stesso calcolo in entrambe le clausole SELECT e GROUP BY. SQL Server sta effettivamente eseguendo questi calcoli due volte o è abbastanza intelligente da farlo solo una volta?

La semplice risposta è che SQL Server non fornisce garanzie generali su quando e quante volte verrà valutata un'espressione scalare al momento dell'esecuzione.

Esistono tutti i tipi di comportamenti complicati (e non documentati) all'interno dell'ottimizzatore e del motore di esecuzione per quanto riguarda il posizionamento, l'esecuzione e la memorizzazione nella cache delle espressioni scalari. Libri online non ha molto da dire al riguardo, ma ciò che dice è questo:

Calcola la descrizione scalare

Questo descrive uno dei comportamenti a cui ho accennato prima, l'esecuzione differita delle espressioni. Ho scritto su alcuni degli altri comportamenti attuali (che potrebbero cambiare in qualsiasi momento) in questo post del blog .

Un'altra considerazione è che il modello di costo utilizzato da Query Optimizer attualmente non fa molto in termini di stima dei costi per le espressioni scalari. Senza un solido quadro dei costi, i risultati attuali si basano su una vasta euristica o pura probabilità.

Per espressioni molto semplici, probabilmente non fa molta differenza se l'espressione viene valutata una o più volte nella maggior parte dei casi. Detto questo, ho riscontrato grandi query in cui le prestazioni sono state influenzate negativamente quando l'espressione viene valutata in modo ridondante un numero molto elevato di volte, oppure la valutazione si verifica su un singolo thread in cui sarebbe stato vantaggioso valutare in un ramo parallelo dell'esecuzione Piano.

In sintesi, l'attuale comportamento non è definito e non c'è molto nei piani di esecuzione per aiutarti a capire cosa è successo (e non sarà sempre conveniente collegare un debugger per esaminare i comportamenti dettagliati del motore, come nel post del blog).

Se si verificano casi in cui i problemi di valutazione scalare sono importanti per le prestazioni, sollevare il problema con il supporto Microsoft. Questo è il modo migliore per fornire feedback per migliorare le versioni future del prodotto.


3

Come afferma il commento sulla tua domanda, la risposta è (almeno nella mia esperienza) "sì". SQL Server è generalmente abbastanza intelligente da evitare il ricalcolo. Probabilmente potresti verificarlo mostrando il piano di esecuzione all'interno di SQL Server Management Studio. Ogni campo calcolato è designato Exprxxxxx(dove xxxxx è un numero). Se sai cosa cercare, dovresti essere in grado di verificare che usi la stessa espressione.

Per aggiungere alla discussione, l'altra opzione estetica è un'espressione da tavolo comune :

with [cte] as
(
    select
        (case when a.type = 'workover' then 'workover' else 
        (case when substring(c.category, 2, 1) = 'd' then 'drilling'
              when substring(c.category, 2, 1) = 'c' then 'completion'
              when substring(c.category, 2, 1) = 'w' then 'workover'
              else 'other' end)
         end)) as [group_key],
         *
    from
        [some_table]
)
select
    [group_key],
    count(*) as [count]
from
    [cte]
group by
    [group_key]

Risposta breve, sono funzionalmente identici a una vista, ma sono validi solo per l'uso nell'istruzione successiva. Li vedo come un'alternativa più leggibile alle tabelle derivate perché evita l'annidamento.

Sebbene non siano pertinenti a questa domanda, possono fare riferimento a se stessi e in tal modo essere utilizzati per costruire query ricorsive.


@Quick Joe Smith: Penso che tu abbia ragione sull'Exprxxxxx, visto che l'ho visto anche io. Tuttavia, se do un nome all'espressione manualmente (case ... end) come OpType, quindi uso il campo OpType nella clausola GROUP BY, ricevo un errore che è un nome di colonna non valido.
Dr. Drew,

Sfortunatamente, la tua unica via d'uscita per specificare l'espressione due volte è usare uno dei metodi sopra: un CTE, una vista o una query nidificata.
Rapido Joe Smith,

2
A meno che tu non sappia anche di CROSS APPLY .
Andriy M,

L'uso cross applyin questo caso è un po 'allungato e molto probabilmente danneggerebbe le prestazioni introducendo un auto-join inutile.
Rapido Joe Smith,

2
Non credo che tu abbia "ricevuto" il suggerimento. Il CROSS APPLYsolo definisce dell'alias da colonne nella stessa riga. Non c'è bisogno di unirsi. ad es.SELECT COUNT(*), hilo FROM master..spt_values CROSS APPLY (VALUES(high + low)) V(hilo) GROUP BY hilo
Martin Smith,

1

Le prestazioni sono solo un aspetto. L'altro è manutenibilità.

Personalmente, tendo a fare quanto segue:

SELECT T.GroupingKey, SUM(T.value)
FROM
(
    SELECT 
        A.*
        (CASE WHEN A.type = 'Workover' THEN 'Workover' ELSE 
        (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' ELSE 'Other' END)
        END) AS GroupingKey
    FROM Table AS A
) AS T

GROUP BY T.GroupingKey

AGGIORNARE:

Se non ti piace eseguire l'annidamento, è possibile creare VISUALIZZA per ogni tabella in cui è necessario utilizzare espressioni complesse.

CREATE VIEW TableExtended
AS 
SELECT 
    A.*
    (CASE WHEN A.type = 'Workover' THEN 'Workover' ELSE 
    (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' ELSE 'Other' END)
    END) AS GroupingKey
FROM Table AS A

Quindi è possibile selezionare senza eseguire ulteriori annidamenti;

SELECT GroupingKey, SUM(value)
FROM TableExtended
GROUP BY GroupingKey
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.