Confronto tra due query in SQL Server 2012


14

Sto confrontando due query in SQL Server 2012. L'obiettivo è utilizzare tutte le informazioni pertinenti disponibili da Query Optimizer quando si sceglie la query migliore. Entrambe le query producono gli stessi risultati; il massimo ordine per tutti i clienti.

La cancellazione del pool di buffer è stata eseguita prima di eseguire ogni query con FREEPROCCACHE e DROPCLEANBUFFERS

Utilizzando le informazioni fornite di seguito, quale query è la scelta migliore?

-- Query 1 - return the maximum order id for a customer
SELECT orderid, custid
FROM Sales.Orders AS O1
WHERE orderid = (SELECT MAX(O2.orderid)
                 FROM Sales.Orders AS O2
                 WHERE O2.custid = O1.custid);


-- Query 2 - return the maximum order id for a customer
SELECT MAX(orderid), custid
FROM Sales.Orders AS O1
group by custid
order by custid

TEMPO STATISTICO

TEMPO STATISTICO query 1: tempo CPU = 0 ms, tempo trascorso = 24 ms

TEMPO STATISTICO query 2: tempo CPU = 0 ms, tempo trascorso = 23 ms

STATISTICA IO

Query 1 STATISTICS IO: Tabella 'Ordini'. Conteggio scansioni 1, letture logiche 5, letture fisiche 2, letture avanti 0, letture logiche lob 0, letture fisiche lob 0, letture read lob 0.

Interrogazione 2 STATISTICS IO: Tabella 'Ordini'. Conteggio scansioni 1, letture logiche 4, letture fisiche 1, letture read-ahead 8, letture logiche lob 0, letture fisiche lob 0, letture read lob 0.

Piani di esecuzione

inserisci qui la descrizione dell'immagine

Proprietà SELECT SELEZIONE 1

inserisci qui la descrizione dell'immagine

Proprietà SELECT Query 2

inserisci qui la descrizione dell'immagine

conclusioni:

Query 1

  1. Costo del lotto 48%
  2. Letture logiche 5
  3. Letture fisiche 2
  4. Read-ahead Letture: 0
  5. Tempo CPU: 0ms
  6. Tempo trascorso 24ms
  7. Costo stimato della sottostruttura: 0,0050276
  8. CompileCPU: 2
  9. CompileMemory: 384
  10. CompileTime: 2

Query 2

  1. Costo del lotto 52%
  2. Letture logiche 4
  3. Letture fisiche 1
  4. Read-ahead Letture: 8
  5. Tempo CPU 0
  6. Tempo trascorso 23ms
  7. Costo stimato della sottostruttura: 0,0054782
  8. CompileCPU: 0
  9. CompileMemory: 192
  10. CompileTime: 0

Personalmente, anche se la Query 2 ha un costo batch più elevato secondo il piano grafico, penso che sia più efficace della Query 1. Questo perché la query 2 richiede meno letture logiche, ha un tempo trascorso leggermente inferiore, i valori compilecpu, compilememory e compiletime sono inferiore. le letture anticipate sono 8 per la query 2 e 0 per la query 1.

Aggiornamento 12:03

Definizione dell'indice cluster

ALTER TABLE [Sales].[Orders] ADD  CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED 
(
    [orderid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

Indice non cluster idx_nc_custid

CREATE NONCLUSTERED INDEX [idx_nc_custid] ON [Sales].[Orders]
(
    [custid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Paul White 9

Risposte:


10

Adoro il tuo approccio alla considerazione attenta all'ottimizzazione delle query e alla revisione delle opzioni e dei piani. Vorrei che altri sviluppatori lo facessero. Un avvertimento sarebbe: prova sempre con molte righe, guardando le letture logiche, questa è una tabella più piccola. Prova a generare un carico di esempio ed esegui nuovamente la query. Un piccolo problema: nella tua query principale non stai richiedendo un ordine, nella query in basso lo sei. Dovresti confrontarli e confrontarli ciascuno con l'ordinamento.

Ho appena creato rapidamente un tavolo SalesOrders con 200.000 ordini di vendita al suo interno - ancora non enorme per qualsiasi tratto di immaginazione. E ha eseguito le query con ORDER BY in ciascuna. Ho anche giocato un po 'con gli indici.

Senza indice cluster su OrderID, solo un indice non cluster su CustID La seconda query ha superato. Soprattutto con l'ordine di incluso in ciascuno. Il numero di letture sulla prima query era doppio rispetto alla seconda query e le percentuali di costo erano del 67% / 33% tra le query.

Con un indice cluster su OrderID e un indice non cluster solo su CustID Hanno eseguito una velocità simile e lo stesso numero esatto di letture.

Quindi suggerirei di aumentare il numero di righe e di fare altri test. Ma la mia analisi finale sulle tue domande -

Potresti trovarli a comportarsi in modo più simile di quanto pensi quando aumenti le file, quindi tieni a mente quell'avvertimento e prova in quel modo.

Se tutto ciò che desideri restituire è il massimo OrderID per ciascun cliente e desideri determinare che l'ID ordine sia il più grande ID ordine, la seconda query su questi due è il modo migliore per passare dalla mia mentalità - è un po ' più semplice e anche se leggermente più costoso in base al costo della sottostruttura, è un'istruzione più semplice e veloce da decifrare. Se hai intenzione di aggiungere altre colonne nel tuo set di risultati un giorno? Quindi la prima query ti consente di farlo.

Aggiornato: uno dei tuoi commenti sotto la tua domanda era:

Tieni presente che trovare la query migliore in questa domanda è un modo per affinare le tecniche utilizzate per confrontarle.

Ma la cosa migliore da fare per fare questo - test con più dati - assicurati sempre di avere dati coerenti con la produzione e la produzione futura prevista. I piani di query iniziano a cercare dati quando si danno più righe alle tabelle e si tenta di mantenere la distribuzione come ci si aspetterebbe in produzione. E presta attenzione a cose come includere Order By o no, qui non credo che faccia una terribile differenza alla fine, ma vale comunque la pena scavare.

Il tuo approccio nel confrontare questo livello di dettaglio e dati è buono. I costi dei sottotree sono per lo più arbitrari e privi di significato, ma vale comunque la pena di cercare almeno un confronto tra modifiche / modifiche o anche tra query. Guardare le statistiche temporali e l'IO è piuttosto importante, così come il piano per tutto ciò che sembra fuori posto per la dimensione dei dati con cui si sta lavorando e ciò che si sta tentando di fare.


Ciao di nuovo, grazie per i tuoi punti sull'utilizzo di maggiori volumi di dati. Questa non è la prima volta che qualcuno lo ha sollevato. L'ultima volta, però, è stato quello di considerare la possibile frammentazione delle suddivisioni di pagina. Nel tuo campione di 200.000 righe, hai verificato la frammentazione?
Craig Efrein,

Bene, nel mio piccolo esempio di 200.000 righe, non mi concentravo sulla frammentazione, no. Ma il modo in cui l'ho fatto non ce ne sarebbe stato. Ho creato una tabella, l'ho popolata e poi ho creato gli indici, quindi sono stati appena creati degli indici. E questo non cambierà l'approccio di guardare i piani di query che sembra essere la domanda principale. Il volume di dati è grande - davvero grande - nell'esame accurato dei piani di query. Ho visto spesso casi in cui sembrava fantastico in sviluppo (con 1-10 righe) ed era orribile in prode con dati reali. Ma il tuo approccio è buono e speriamo che queste informazioni e la conversazione nei commenti siano d'aiuto
Mike Walsh,

Dal momento che stiamo raggruppando per custid, come hai reso i valori di custid abbastanza casuali? Una cosa che ricordo dalle mie letture è l'importanza di valori distinti. Se custid avesse solo un numero limitato di clienti distinti, il costo per l'aggregato del flusso sarebbe irrealistico.
Craig Efrein,

Ho appena usato la funzione RAND per creare 100 clienti e assegnarne uno a caso a ciascun ID ordine. Stavo facendo un rapido controllo. :)
Mike Walsh,

Grazie Mike per tutto il tuo aiuto. Un'ultima domanda però. Dalle schermate delle proprietà SELECT del Piano di esecuzione nel 2012 che ho fornito nella mia domanda, a quali valori presti attenzione?
Craig Efrein,
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.