Sono questi croce mi raggiunge causando problemi?
-
21-08-2019 - |
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?
PianiEsecuzione:
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]))
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_table
né q.id
nel piano, a soli due 's. #tmp
E altre due domande:
- È
yes
unCLUSTERED
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.