2013-01-08 15 views
14

Ho la query sql sotto che è in esecuzione molto lentamente. Ho dato un'occhiata al piano di esecuzione e sto sostenendo che un ordinamento su Files.OrderId è l'operazione con il costo più alto (53%). Perché questo dovrebbe accadere se non ordino da OrderId ovunque? La mia migliore scommessa per creare un indice su File.OrderId?Perché nel mio piano di esecuzione è presente un ordinamento?

Execution plan se qualcuno è interessato.

with custOrders as 
(
    SELECT c.firstName + ' ' + c.lastname as Customer, c.PartnerId , c.CustomerId,o.OrderId,o.CreateDate, c.IsPrimary 
    FROM Customers c 
    LEFT JOIN CustomerRelationships as cr 
     ON c.CustomerId = cr.PrimaryCustomerId 
    INNER JOIN Orders as o 
     ON c.customerid = o.customerid 
      OR (cr.secondarycustomerid IS NOT NULL AND o.customerid = cr.secondarycustomerid) 
    where c.createdate >= @FromDate + ' 00:00' 
     AND c.createdate <= @ToDate + ' 23:59' 
), 
temp as 
(
SELECT Row_number() 
     OVER ( 
      ORDER BY c.createdate DESC)     AS 'row_number', 
     c.customerid as customerId, 
     c.partnerid as partnerId, 
     c.Customer, 
     c.orderid as OrderId, 
     c.createdate as CreateDate, 
     Count(f.orderid)         AS FileCount, 
     dbo.Getparentcustomerid(c.isprimary, c.customerid) AS ParentCustomerId, 
     au.firstname + ' ' + au.lastname     AS Admin, 
     '' as blank, 
     0 as zero 
FROM custOrders c 
     INNER JOIN files f 
       ON c.orderid = f.orderid 
     INNER JOIN admincustomers ac 
       ON c.customerid = ac.customerid 
     INNER JOIN adminusers au 
       ON ac.adminuserid = au.id 
     INNER JOIN filestatuses s 
       ON f.statusid = s.statusid 
WHERE ac.adminuserid IS NOT NULL 
     AND f.statusid NOT IN (5, 6) 
GROUP BY c.customerid, 
      c.partnerid, 
      c.Customer, 
      c.isprimary, 
      c.orderid, 
      c.createdate, 
      au.firstname, 
      au.lastname 
) 
+0

* Errore durante la convalida XML di origine * – Kermit

+0

Sì ho visto che. Fa davvero differenza? È ancora possibile vedere l'XML ... –

+0

L'opzione "OVER" non implica un ordinamento? – Kermit

risposta

11

SQL Server dispone di tre algoritmi tra cui scegliere quando è necessario unire due tabelle. I loop nested-Join, il Hash-Join e il Sort-Merge-Join. Quale si seleziona si basa sulle stime dei costi. In questo caso ha calcolato che, in base alle informazioni disponibili, Sort-Merge-Join era la scelta giusta.

Nei piani di esecuzione di SQL Server, l'unione di ordinamento è suddivisa in due operatori, l'ordinamento e l'unione-unione, poiché l'operazione di ordinamento potrebbe non essere necessaria, ad esempio se i dati sono già ordinati.

Per informazioni mor sulla unisce controllare il mio serie di unirsi qui: http://sqlity.net/en/1146/a-join-a-day-introduction/ L'articolo sul Sort-Merg-join è qui: http://sqlity.net/en/1480/a-join-a-day-the-sort-merge-join/


Per rendere la query più veloce, in primo luogo ho guardavo gli indici . Hai un sacco di scansioni di indice cluster nella query. Se è possibile sostituirne alcuni con ricerche, molto probabilmente lo farai meglio. Controllare anche se le stime prodotte da SQL Server corrispondono ai conteggi delle righe effettivi in ​​un piano di esecuzione effettivo. Se sono lontani, SQL Server spesso fa scelte sbagliate. Pertanto, fornire statistiche migliori può aiutarti anche a migliorare le prestazioni.

+0

+1 per i conteggi effettivi delle righe e le statistiche –

1

penso che il genere è in corso per questo aderire:

FROM custOrders c 
     INNER JOIN files f 
       ON c.orderid = f.orderid 

vorrei creare un indice su file che include le colonne OrderID e StatusId dal momento che la query utilizza anche la colonna StatusId.

Si potrebbe anche prendere in considerazione le seguenti modifiche:

  1. Non è necessario "ac.adminuserid non è nullo", in quanto questo è coperto dal join interno tra adminusers e admincustomers
  2. Cambiare la testare "f.statusid NOT IN (5, 6)" in una condizione positiva (ad esempio In) poiché le condizioni negative sono più costose da elaborare.
+0

[file] ha già un indice di copertura –

+0

Sì, sta già facendo una ricerca su indice non in cluster su 'File'. C'è un modo per ridurre ulteriormente il numero di record provenienti da 'Files'? –

+0

Una ricerca per indici non in cluster va bene dal punto di vista delle prestazioni. L'altra modifica per ridurre il conteggio dei record consiste nell'eliminare il controllo NOT IN rispetto a statusid come consigliato sopra. –

2

SQL Server sta eseguendo l'ordinamento per abilitare il collegamento unione tra il set di dati a destra dell'operatore di ordinamento e i record nella tabella Orders. Unisci join stesso è un modo molto efficace per unire tutti i record in un set di dati, ma richiede che ogni set di dati da unire sia ordinato in base alle chiavi di join e nello stesso ordine.

Dal momento che la chiave PK_Orders è già ordinato da OrderID, SQL Server ha deciso di approfittare di quel di classificare l'altra estremità del join (le altre cose alla destra del genere), in modo che i due gruppi di dati possono essere fusi insieme a quel punto del piano. L'alternativa comune per unire join è un hash join, ma questo non ti sarebbe di aiuto perché avresti invece un costoso operatore di hash join invece dell'ordinamento e dell'unione. Query Optimizer ha determinato l'ordinamento e l'unione per essere più efficiente in questo caso.

La causa principale del costoso passaggio nel piano è la necessità di combinare tutti i record dalla tabella degli ordini nel set di dati. C'è un modo per limitare i record provenienti dalla tabella files? Un indice su files.statusid può essere utile se i record non in 5,6 sono inferiori al 10% della dimensione totale della tabella.

Il QO pensa che la maggior parte dei record verranno filtrati alla fine. Cerca di spingere tante di queste condizioni di filtro alle origini dei record in modo che meno documenti debbano essere gestiti nel mezzo del piano.

MODIFICA: Ho dimenticato di menzionare, è molto utile avere un piano di esecuzione che possiamo guardare. C'è un modo per ottenere un risultato effettivo del piano di esecuzione per vedere il numero reale di record che attraversano quegli operatori? A volte i conteggi dei record stimati possono essere un po 'spenti.

EDIT: Guardando più in profondità 2 al campo predicato della scorsa operatore di filtro, riassunta:

c.CustomerId=o.CustomerId 
OR o.CustomerId=cr.SecondaryCustomerId AND cr.SecondaryCustomerId IS NOT NULL 

Sembra che SQL Server sta producendo un cross join tra tutti i possibili record corrispondenti tra Orders e Customers fino a questo puntare nella query (il piano a destra del 2 ° all'ultimo operatore di filtro) e quindi esaminare ciascun record con quella condizione per vedere se effettivamente corrisponde. Nota come la linea che entra nel filtro è davvero grassa e la linea che esce è davvero sottile? Questo perché il numero di righe stimato va da 21k a 4 dopo quell'operatore. Dimentica quello che ho detto prima, questo è probabilmente il problema principale del piano. Anche se esistono indici su queste colonne, SQL Server non può utilizzarli perché la condizione di join è troppo complessa. Sta facendo sì che il piano unisca tutti i record insieme invece di cercare solo quelli necessari, poiché non può utilizzare subito il predicato completo del join.

Il mio primo pensiero è riformulare il CTE custOrders come unione di due set di dati: uno con CustomerId e uno con SecondaryCustomerId da unire. Questo duplicherà il lavoro del resto del CTE ma se abilita l'uso corretto degli indici, potrebbe essere una grande vittoria.

+0

Ok, ho aggiornato con il piano attuale –

+0

Sembra ancora il piano originale stimato. Hai modificato la domanda o modificato il documento su XML Playground? –

0

So che questa domanda è piuttosto vecchia, tuttavia ho avuto lo stesso problema e ho capito che c'era un motivo completamente diverso che le mie tabelle improvvisamente rallentavano. I sintomi erano gli stessi, lenti ad aggiornare le viste che in precedenza erano fulminei. "Ordina" con un costo del 40%. Questa soluzione può rivelarsi utile a qualcuno, ed è semplice. Quando si uniscono le tabelle, assicurarsi di unirsi a una base di tipo "like for like". Stavo unendo due tabelle su ID. Comunque in una tabella il mio ID è stato impostato come int e nell'altro come nvarchar. Ho corretto questo per averli entrambi definiti come lo stesso tipo e la vista è tornata alla velocità della luce.

speriamo che questo possa aiutare qualcun altro a evitare di trascorrere una settimana cercando di capire cosa c'è di sbagliato in SQL, quando è davvero un momento PEBKAC.

(problema esiste tra la tastiera e sedia)

Problemi correlati