2010-03-24 15 views
72

Confronta queste 2 domande. È più veloce inserire il filtro nei criteri di join o nella clausola were. Ho sempre pensato che fosse più veloce nei criteri di join perché riduce il set di risultati nel più breve tempo possibile, ma non ne sono sicuro.Quale query SQL è più veloce? Filtro sui criteri di partecipazione o clausola Where?

Ho intenzione di costruire alcuni test per vedere, ma volevo anche ottenere opinioni su cui sarebbe più chiaro da leggere.

Query 1

SELECT  * 
FROM  TableA a 
INNER JOIN TableXRef x 
     ON a.ID = x.TableAID 
INNER JOIN TableB b 
     ON x.TableBID = b.ID 
WHERE  a.ID = 1   /* <-- Filter here? */ 

Query 2

SELECT  * 
FROM  TableA a 
INNER JOIN TableXRef x 
     ON a.ID = x.TableAID 
     AND a.ID = 1   /* <-- Or filter here? */ 
INNER JOIN TableB b 
     ON x.TableBID = b.ID 

EDIT

Ho eseguito alcuni test ed i risultati mostrano che in realtà è molto vicino, ma il La clausola WHERE è in realtà ridotta molto più veloce! =)

Sono assolutamente d'accordo sul fatto che abbia più senso applicare il filtro sulla clausola WHERE, ero solo curioso di sapere le implicazioni sulle prestazioni.

TEMPO TRASCORSO DOVE CRITERI: 143.016 ms
TEMPO TRASCORSO criteri di join: 143.256 ms

TEST

SET NOCOUNT ON; 

DECLARE @num INT, 
     @iter INT 

SELECT @num = 1000, -- Number of records in TableA and TableB, the cross table is populated with a CROSS JOIN from A to B 
     @iter = 1000 -- Number of select iterations to perform 

DECLARE @a TABLE (
     id INT 
) 

DECLARE @b TABLE (
     id INT 
) 

DECLARE @x TABLE (
     aid INT, 
     bid INT 
) 

DECLARE @num_curr INT 
SELECT @num_curr = 1 

WHILE (@num_curr <= @num) 
BEGIN 
    INSERT @a (id) SELECT @num_curr 
    INSERT @b (id) SELECT @num_curr 

    SELECT @num_curr = @num_curr + 1 
END 

INSERT  @x (aid, bid) 
SELECT  a.id, 
      b.id 
FROM  @a a 
CROSS JOIN @b b 

/* 
    TEST 
*/ 
DECLARE @begin_where DATETIME, 
     @end_where  DATETIME, 
     @count_where INT, 
     @begin_join  DATETIME, 
     @end_join  DATETIME, 
     @count_join  INT, 
     @curr   INT, 
     @aid   INT 

DECLARE @temp TABLE (
     curr INT, 
     aid  INT, 
     bid  INT 
) 

DELETE FROM @temp 

SELECT @curr = 0, 
     @aid = 50 

SELECT @begin_where = CURRENT_TIMESTAMP 
WHILE (@curr < @iter) 
BEGIN 
    INSERT  @temp (curr, aid, bid) 
    SELECT  @curr, 
       aid, 
       bid 
    FROM  @a a 
    INNER JOIN @x x 
      ON a.id = x.aid 
    INNER JOIN @b b 
      ON x.bid = b.id 
    WHERE  a.id = @aid 

    SELECT @curr = @curr + 1 
END 
SELECT @end_where = CURRENT_TIMESTAMP 

SELECT @count_where = COUNT(1) FROM @temp 
DELETE FROM @temp 

SELECT @curr = 0 
SELECT @begin_join = CURRENT_TIMESTAMP 
WHILE (@curr < @iter) 
BEGIN 
    INSERT  @temp (curr, aid, bid) 
    SELECT  @curr, 
       aid, 
       bid 
    FROM  @a a 
    INNER JOIN @x x 
      ON a.id = x.aid 
      AND a.id = @aid 
    INNER JOIN @b b 
      ON x.bid = b.id 

    SELECT @curr = @curr + 1 
END 
SELECT @end_join = CURRENT_TIMESTAMP 

SELECT @count_join = COUNT(1) FROM @temp 
DELETE FROM @temp 

SELECT @count_where AS count_where, 
     @count_join AS count_join, 
     DATEDIFF(millisecond, @begin_where, @end_where) AS elapsed_where, 
     DATEDIFF(millisecond, @begin_join, @end_join) AS elapsed_join 
+8

A seconda dei dati, i criteri WHERE vs JOIN possono restituire set di risultati diversi. –

+2

@OMG Ponies molto vero, ma molte volte non lo è altrettanto. –

+2

Non chiamerei differenza di meno del 5% come differenza: sono uguali. Volete un significato per una differenza del 2 %%, meglio eseguire i test 1000 volte per assicurarsi che non sia solo casuale. – TomTom

risposta

50

delle prestazioni, sono la stessa (e producono gli stessi piani)

Logicamente, si dovrebbe fare l'operazione che ha ancora un senso se si sostituisce con un INNER JOINLEFT JOIN.

Nel vostro molto caso sarà simile a questa:

SELECT * 
FROM TableA a 
LEFT JOIN 
     TableXRef x 
ON  x.TableAID = a.ID 
     AND a.ID = 1 
LEFT JOIN 
     TableB b 
ON  x.TableBID = b.ID 

o questo:

SELECT * 
FROM TableA a 
LEFT JOIN 
     TableXRef x 
ON  x.TableAID = a.ID 
LEFT JOIN 
     TableB b 
ON  b.id = x.TableBID 
WHERE a.id = 1 

L'ex query non restituirà alcun match effettivi per a.id diverso 1, in modo da quest'ultimo sintassi (con WHERE) è logicamente più coerente.

+0

Quando disegno i set ho capito perché il secondo caso è più coerente. Nella precedente query, il vincolo 'a.id = 1' si applica solo per l'intersezione, non la parte sinistra che esclude l'intersezione. – FtheBuilder

+1

Nel primo esempio ci possono essere righe dove 'a.id! = 1', l'altra avrà solo righe dove' a.id = 1'. – FtheBuilder

+0

La tua lingua non è chiara. "Logicamente, dovresti fare l'operazione che ha ancora senso se ..." e "logicamente più coerente" non hanno senso. Puoi riformulare per favore? – philipxy

0

Credo che il primo, perché rende una più specifica filtra sopra i dati. Ma è lo should see the execution plan, come con qualsiasi ottimizzazione, perché può essere molto diverso a seconda della dimensione dei dati, dell'hardware del server, ecc.

0

È più veloce? Provalo e guarda

Quale è più facile da leggere? Il primo a me sembra più "corretto", poiché la condizione spostata non ha nulla a che fare con il join.

+0

Oh, mi dispiace amico !!! – Quassnoi

2

Con qualsiasi ottimizzatore di query worh un centesimo .... sono identici.

+0

Sono abbastanza sicuro che, con qualsiasi carico di lavoro reale, non sono identici. Se quasi non hai dati, la domanda è inutile. – eKek0

+1

Controllalo sotto carico di lavoro reale. Fondamentalmente - se generano lo stesso piano di esecuzione, sono ... identici nelle prestazioni. Almeno per i casi normali/semplici (ad esempio non quello che unisce 14 tabelle) Sono abbastanza sicuro che siano identici;) – TomTom

7

Per quanto riguarda i due metodi.

  • ENTRA/ON è per le tabelle che aderiscono
  • casi in cui è per il filtraggio risultati

Mentre si possono usare in modo diverso sembra sempre come un odore per me.

Affrontare le prestazioni quando si tratta di un problema. Quindi puoi guardare a tali "ottimizzazioni".

0

È davvero improbabile che il posizionamento di questa unione costituisca il fattore decisivo per le prestazioni. Non ho familiarità con la pianificazione dell'esecuzione di tsql, ma è probabile che verranno automaticamente ottimizzati per piani simili.

0

Regola n. 0: eseguire alcuni parametri di riferimento e vedere! L'unico modo per capire veramente quale sarà più veloce è provarlo. Questi tipi di benchmark sono molto facili da eseguire utilizzando il profiler SQL.

Inoltre, esaminare il piano di esecuzione per la query scritta con un JOIN e con una clausola WHERE per vedere quali differenze si evidenziano.

Infine, come altri hanno detto, questi due devono essere trattati in modo identico da qualsiasi ottimizzatore decente, incluso quello integrato in SQL Server.

+0

Ma solo per i join interni. I risultati saranno molto diversi per i join. – HLGEM

+0

Ovviamente. Fortunatamente, l'esempio fornito usa inner join. –

+1

Sfortunatamente la domanda riguarda i join, non i join interiori. – Paul

18

Per i join interni non importa dove si mettono i criteri. Il compilatore SQL trasformerà entrambi in un piano di esecuzione in cui il filtraggio si verifica al di sotto del join (ad esempio, come se le espressioni del filtro appaiano nella condizione di join).

I join esterni sono un argomento diverso, poiché la posizione del filtro modifica la semantica della query.

+0

Quindi nei join interni per prima cosa calcola il filtro e quindi unisce l'output del filtro con l'altra tabella o prima unisce le due tabelle e quindi applica il filtro? – Ashwin

+0

@Rusus Rusanu - potresti per favore approfondire come la semantica cambierà in caso di Outer-join? Ottengo risultati diversi in base alla posizione del filtro, ma non riesco a capire perché – Ananth

+1

@Ananth con un join esterno ottieni NULL per tutte le colonne della tabella unita in cui la condizione JOIN non corrisponde. I filtri non soddisfano il NULL ed eliminano le righe, trasformando l'OUTER in join in un join INNER. –

Problemi correlati