2013-03-15 16 views
5

Perché è questa query più veloce in SQL Server 2008 R2 (Version 10.50.2806.0)query più velocemente con l'attributo top

SELECT 
     MAX(AtDate1), 
     MIN(AtDate2) 
    FROM 
    (
     SELECT TOP 1000000000000 
      at.Date1 AS AtDate1, 
      at.Date2 AS AtDate2 
     FROM 
      dbo.tab1 a 
     INNER JOIN 
      dbo.tab2 at 
     ON 
      a.id = at.RootId 
     AND CAST(GETDATE() AS DATE) BETWEEN at.Date1 AND at.Date2 
     WHERE 
      a.Number = 223889 
    )B 

poi

SELECT 
     MAX(AtDate1), 
     MIN(AtDate2) 
    FROM 
    (
     SELECT 
      at.Date1 AS AtDate1, 
      at.Date2 AS AtDate2 
     FROM 
      dbo.tab1 a 
     INNER JOIN 
      dbo.tab2 at 
     ON 
      a.id = at.RootId 
     AND CAST(GETDATE() AS DATE) BETWEEN at.Date1 AND at.Date2 
     WHERE 
      a.Number = 223889 
    )B 

?

La seconda dichiarazione con l'attributo TOP è sei volte più veloce.

La count(*) della sottoquery interna è 9280 righe.

Posso usare un suggerimento per dichiarare che ottimizzatore di SQL Server rendono giusto? execution plan

+0

Dovrai dare un'occhiata al piano di esecuzione per essere sicuro. È probabile che stia prendendo un percorso più efficiente con il TOP che senza. – Khan

+2

Questo potrebbe essere causato da un problema di ottimizzazione. Potete fornire piani di esecuzione per entrambe le domande? –

+3

Il modo migliore per fornire i piani di esecuzione è eseguirli in SSMS con l'opzione "Query -> Includi piano di esecuzione reale" abilitata, quindi caricare la versione XML in un sito come pastebin. Vedi [Come posso fornire un piano di esecuzione a qualcuno per l'analisi?] (Http://meta.dba.stackexchange.com/questions/796/how-do-i-provide-an-execution-plan-to-someone- for-analysis) per di più. –

risposta

3

ti vedo ora ho postato the plans. Solo fortuna del sorteggio.

La tua query effettiva è un 16 join tabella.

SELECT max(atDate1) AS AtDate1, 
     min(atDate2) AS AtDate2, 
     max(vtDate1) AS vtDate1, 
     min(vtDate2) AS vtDate2, 
     max(bgtDate1) AS bgtDate1, 
     min(bgtDate2) AS bgtDate2, 
     max(lftDate1) AS lftDate1, 
     min(lftDate2) AS lftDate2, 
     max(lgtDate1) AS lgtDate1, 
     min(lgtDate2) AS lgtDate2, 
     max(bltDate1) AS bltDate1, 
     min(bltDate2) AS bltDate2 
FROM (SELECT TOP 100000 at.Date1 AS atDate1, 
          at.Date2 AS atDate2, 
          vt.Date1 AS vtDate1, 
          vt.Date2 AS vtDate2, 
          bgt.Date1 AS bgtDate1, 
          bgt.Date2 AS bgtDate2, 
          lft.Date1 AS lftDate1, 
          lft.Date2 AS lftDate2, 
          lgt.Date1 AS lgtDate1, 
          lgt.Date2 AS lgtDate2, 
          blt.Date1 AS bltDate1, 
          blt.Date2 AS bltDate2 
     FROM dbo.Tab1 a 
       INNER JOIN dbo.Tab2 at 
       ON a.id = at.Tab1Id 
        AND cast(Getdate() AS DATE) BETWEEN at.Date1 AND at.Date2 
       INNER JOIN dbo.Tab5 v 
       ON v.Tab1Id = a.Id 
       INNER JOIN dbo.Tab16 g 
       ON g.Tab5Id = v.Id 
       INNER JOIN dbo.Tab3 vt 
       ON v.id = vt.Tab5Id 
        AND cast(Getdate() AS DATE) BETWEEN vt.Date1 AND vt.Date2 
       LEFT OUTER JOIN dbo.Tab4 vk 
       ON v.id = vk.Tab5Id 
       LEFT OUTER JOIN dbo.VerkaufsTab3 vkt 
       ON vk.id = vkt.Tab4Id 
       LEFT OUTER JOIN dbo.Plu p 
       ON p.Tab4Id = vk.Id 
       LEFT OUTER JOIN dbo.Tab15 bg 
       ON bg.Tab5Id = v.Id 
       LEFT OUTER JOIN dbo.Tab7 bgt 
       ON bgt.Tab15Id = bg.Id 
        AND cast(Getdate() AS DATE) BETWEEN bgt.Date1 AND bgt.Date2 
       LEFT OUTER JOIN dbo.Tab11 b 
       ON b.Tab15Id = bg.Id 
       LEFT OUTER JOIN dbo.Tab14 lf 
       ON lf.Id = b.Id 
       LEFT OUTER JOIN dbo.Tab8 lft 
       ON lft.Tab14Id = lf.Id 
        AND cast(Getdate() AS DATE) BETWEEN lft.Date1 AND lft.Date2 
       LEFT OUTER JOIN dbo.Tab13 lg 
       ON lg.Id = b.Id 
       LEFT OUTER JOIN dbo.Tab9 lgt 
       ON lgt.Tab13Id = lg.Id 
        AND cast(Getdate() AS DATE) BETWEEN lgt.Date1 AND lgt.Date2 
       LEFT OUTER JOIN dbo.Tab10 bl 
       ON bl.Tab11Id = b.Id 
       LEFT OUTER JOIN dbo.Tab6 blt 
       ON blt.Tab10Id = bl.Id 
        AND cast(Getdate() AS DATE) BETWEEN blt.Date1 AND blt.Date2 
     WHERE a.Nummer = 223889) B 

Su entrambi i piani di buoni e cattivi il piano di esecuzione mostra "La ragione per la risoluzione anticipata della Dichiarazione Optimization" come "Time Out".

I due piani hanno ordini di join leggermente diversi.

L'unica unirsi nei piani non soddisfatte da un indice cercare è che il Tab9. Questo ha 63.926 righe.

I dettagli di indice mancanti nel piano di esecuzione suggeriscono di creare il seguente indice.

CREATE NONCLUSTERED INDEX [miising_index] 
ON [dbo].[Tab9] ([Date1],[Date2]) 
INCLUDE ([Tab13Id]) 

La parte problematica del cattivo piano può essere visto chiaramente in SQL Plan Sentry Explorer

Bad Plan

SQL Server stima che 1.349174 righe verranno restituiti dal precedente unisce entrano in join su Tab9. Di conseguenza, i loop annidati vengono uniti come se fosse necessario eseguire la scansione nella tabella interna 1.349174 volte.

In effetti, 2600 righe vengono immesse in quel join, il che significa che esegue 2.600 scansioni complete di Tab9 (2.600 * 63.926 = 164.569.600 righe.)

Si dà il caso che sul buon piano il numero stimato di righe in arrivo al join sia 2.74319. Ciò è ancora errato di tre ordini di grandezza ma la stima leggermente aumentata significa che SQL Server preferisce invece un join hash. Un hash join solo fa un solo passaggio attraverso Tab9

Good Plan

Desidero in primo luogo provare ad aggiungere l'indice mancante su Tab9.

Inoltre/invece si potrebbe provare ad aggiornare le statistiche su tutte le tabelle coinvolte (in particolare quelli con una data predicato come Tab2Tab3Tab7Tab8Tab6) e vedere se questo va in qualche modo a correggere l'enorme discrepanza tra le righe previste e quelle effettive su la sinistra del piano.

Anche rompere la query in parti più piccole e materializzarle in tabelle temporanee con indici appropriati può essere d'aiuto. SQL Server può quindi utilizzare le statistiche su questi risultati parziali per prendere decisioni migliori per i join più avanti nel piano.

Solo come ultima risorsa considererei l'utilizzo di suggerimenti per le query per provare a forzare il piano con un hash join. Le opzioni disponibili sono l'avviso USE PLAN, nel qual caso è necessario dettare esattamente il piano che si desidera includere tutti i tipi di join e gli ordini o dichiarando LEFT OUTER HASH JOIN tab9 .... Questa seconda opzione ha anche l'effetto collaterale di correggere tutti gli ordini di join nel piano. Entrambi significano che SQL Server sarà gravemente limitato è la sua capacità di regolare il piano con modifiche nella distribuzione dei dati.

1

È difficile rispondere non conoscendo la dimensione e la struttura delle tabelle e non essere in grado di visualizzare l'intero piano di esecuzione. Ma la differenza in entrambi i piani è Hash Match join per "top n" query vs Nested Loop join per l'altro. Hash Match è un join con molte risorse, in quanto il server deve preparare i bucket hash per poterlo utilizzare. Ma diventa molto più efficace per le grandi tabelle, mentre Nested Loops, confrontando ogni riga di una tabella con ogni riga di un'altra tabella, funziona perfettamente per i tavoli di piccole dimensioni, perché non è necessaria alcuna preparazione del genere. Quello che penso è che selezionando TOP 1000000000000 righe nella sottoquery si dia all'ottimizzatore il suggerimento che la subquery produrrà una grande quantità di dati, quindi usa Hash Match. Ma in realtà l'output è piccolo, quindi Nested Loops funziona meglio. Quello che ho appena detto si basa su brandelli di informazioni, quindi per favore abbiate cuore criticando la mia risposta;).

+0

questo è il piano di esecuzione per le query http://www.dateiupload.net/download.php?file = 0875b65fb14d01c90b48c5fd146a36e3 – JJANSSEN

+0

grazie per la risposta. come posso dare un suggerimento all'ottimizzatore per usare un hash join, quindi posso rimuovere l'attributo top – JJANSSEN

Problemi correlati