Domanda

Ho installato la replica di un uomo povero che non posso fare nulla. Alcuni dati identificativi (chiave primaria in fondo) da un call_table vengono copiati in un'altra tabella tramite un semplice scatto, e poi il "server di replica" corre una stored procedure per copiare i dati dalla tabella coda per un tavolo #temp (per evitare blocco in SQL 6.5 è il caso che è stato fatto a me). Infine, una query utilizza i dati chiave della tabella temporanea per estrarre i dati al server di replica dal <=> usando questa query:

  /* select the data to return to poor man replication server */     
  SELECT c.id,
         c.date,
         c.time,
         c.duration,
         c.location
    FROM #tmp q, call_table c (NOLOCK)
   WHERE q.id=c.id  
     AND q.date=c.date
     AND q.time=c.time
     AND q.duration=c.duration
     AND q.location=c.location
GROUP BY c.id,
         c.date,
         c.time,
         c.duration,
         c.location

Una volta che una notte la tabella di coda viene eliminato e questo ricomincia. Mentre indaga questo, la croce implicita join saltato su di me (io sono sul lato che di solito sono il male), ma poi ho letto la potenza della Croce Entra . Sono qui perché non sono del tutto convinto. Di 'la tabella temporanea ha circa 10.000 righe per il giorno, la call_table ha circa 100.000 per il mese finora. Come è questa query di andare a lavorare? Ha schiacciare i due tavoli insieme per un totale di 1000 milioni in memoria, quindi utilizzare la clausola gruppo di tagliare di nuovo verso il basso? Ci può spiegare quali passi SQL serve per compilare i risultati?

Piani

Esecuzione:

My Query:
      |--Hash Match Root(Aggregate, HASH:([c].[id], [c].[date], [c].[location], [c].[time], [c].[duration]), RESIDUAL:(((((((((((((((((((((([c].[id]=[c].[id] AND [c].[PIN]=[c].[PIN]) AND [c].[ORIG]=[c].[ORIG]) AND [c].[date]=[c].[date]) AND [c].[CTIME]=[c].[CTIME
           |--Hash Match Team(Inner Join, HASH:([q].[id], [q].[date], [q].[location], [q].[time], [q].[duration])=([c].[id], [c].[date], [c].[location], [c].[time], [c].[duration]), RESIDUAL:(((([c].[id]=[q].[id] AND [c].[location]=[q].[location]) AND [c].[duration]=[q].[duration]) AND [
                |--Table Scan(OBJECT:([db].[dbo].[queue] AS [q]))
                |--Table Scan(OBJECT:([db].[dbo].[call_table] AS [c]))

Yours:              
  |--Merge Join(Right Semi Join, MERGE:([q].[id], [q].[date], [q].[time], [q].[duration], [q].[location])=([c].[id], [c].[date], [c].[time], [c].[duration], [c].[location]), RESIDUAL:(((([q].[id]=[c].[id] AND [q].[location]=[c].[location]) AND [q].[duration]=[c].[duration]) AND [q].[
       |--Index Scan(OBJECT:([db].[dbo].[queue].[PK_queue] AS [q]), ORDERED)
       |--Sort(ORDER BY:([c].[id] ASC, [c].[date] ASC, [c].[time] ASC, [c].[duration] ASC, [c].[location] ASC))
            |--Table Scan(OBJECT:([db].[dbo].[call_table] AS [c]))
È stato utile?

Soluzione

La query che hai descritto è alcun modo un CROSS JOIN.

SQL Server è abbastanza intelligente per trasformare la condizione di WHERE nella s 'il JOIN.

Tuttavia, non vedo alcun punto nel GROUP BY qui.

Questa query:

  SELECT c.id,
         c.date,
         c.time,
         c.duration,
         c.location
    FROM #tmp q, call_table c (NOLOCK)
   WHERE q.id=c.id  
     AND q.date=c.date
     AND q.time=c.time
     AND q.duration=c.duration
     AND q.location=c.location
GROUP BY c.id,
         c.date,
         c.time,
         c.duration,
         c.location

può essere riscritta come easilty

  SELECT  c.id,
          c.date,
          c.time,
          c.duration,
          c.location
  FROM    call_table c (NOLOCK)
  WHERE   EXISTS
          (
          SELECT  NULL
          FROM    #tmp q
          WHERE   q.id = c.id  
                  AND q.date = c.date
                  AND q.time = c.time
                  AND q.duration = c.duration
                  AND q.location = c.location
          )

, purché c.id è un PRIMARY KEY.

Se non lo è, basta aggiungere DISTINCT a SELECT sopra.

Aggiornamento:

Da tuo piano vedo che che la query utilizza HASH JOIN, mentre i miei usi MERGE SEMI JOIN.

Quest'ultimo è di solito più efficace se si dispone di un insieme ordinato, ma per qualche motivo la query non utilizza l'indice composito si è creato, ma svolge invece scansione completa della tabella.

Questo è strano, dal momento che tutti i valori sono contenuti all'interno dell'indice.

Probabilmente (probabilmente) questo è perché i vostri campi consentono NULL 's.

Assicurarsi di utilizzare solo i campi dall'indice composito sia in NOT NULL condizioni ed in TABLE SCAN clausola e, se possibile, li rendono SORT.

Questo dovrebbe rendere l'uso di query preordinato di risultati in INDEX SCAN. Si può dire che se si vede né call_tableq.id nel piano, a soli due 's. #tmp

E altre due domande:

  • È yes un CLUSTERED su <=>?
  • È <=> un <=> su <=>?

Se la risposta ad entrambe le domande è <=>, allora si potranno beneficiare di fare due cose:

  • Definizione del <=> come <=> su entrambe le tabelle
  • Riscrivere la query come questa:

     SELECT  c.id,
             c.date,
             c.time,
             c.duration,
             c.location
     FROM    call_table c (NOLOCK)
     JOIN    #tmp q
     ON      q.id = c.id  
             AND q.date = c.date
             AND q.time = c.time
             AND q.duration = c.duration
             AND q.location = c.location
    

Altri suggerimenti

  

Come è questa query di andare a lavorare? fa   esso schiacciare le due tabelle insieme per un   totale di 1000 milioni in memoria, allora   utilizzare la clausola gruppo per tagliare indietro   giù? Ci può spiegare quali passi SQL   prende per compilare i risultati?

Si potrebbe andare qualcosa come questo. Di 'Sql Server decide di utilizzare un hash join. Si crea una tabella hash in memoria di #temp, con un hash basato su ID, data, ora, durata e ubicazione. Poi si itera sopra le righe in call_table. Per ogni riga, si utilizza la tabella hash per rilevare se esiste una riga corrispondente. Se lo fa, la riga viene aggiunta alla tabella dei risultati. Quindi non 1.000.000.000 di righe sono sempre nella memoria.

Un'altra opzione (forse anche meglio qui) è quello di iterare su una tabella, e utilizzare la colonna id di fare una ricerca di indice sull'altro tavolo. Questo richiede anche meno memoria (anche se sarebbe molto vantaggioso se l'indice fosse nella cache.)

Si può vedere ciò che SQL Server non veramente leggendo il piano di esecuzione. È possibile attivare il piano di esecuzione sotto il menu Query.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top