2010-10-21 13 views
5

Ho due tabelle - incoming tours(id,name) e incoming_tours_cities(id_parrent, id_city)bisogno di aiuto nell'ottimizzazione domanda

id nella prima tabella è unico, e per ogni riga univoco da prima tabella c'è l'elenco dei id_city - s nella seconda tabella (cioè id_parrent in seconda tabella è uguale a id dalla prima tabella)

ad esempio

incoming_tours

|--id--|------name-----| 
|---1--|---first_tour--| 
|---2--|--second_tour--| 
|---3--|--thirth_tour--| 
|---4--|--hourth_tour--| 

incoming_tours_cities

|-id_parrent-|-id_city-| 
|------1-----|---4-----| 
|------1-----|---5-----| 
|------1-----|---27----| 
|------1-----|---74----| 
|------2-----|---1-----| 
|------2-----|---5-----| 
........................ 

Ciò significa che first_tour ha elenco di città - ("4","5","27","74")

E second_tour ha elenco di città - ("1","5")


Supponiamo che io ho due valori - 4 e 74:

Ora, ho bisogno di ottenere tutte le righe dalla prima tabella, dove i miei valori sono compresi nell'elenco nell'elenco delle città. cioè deve restituire solo il first_tour (perché 4 e 74 sono nella sua lista delle città)

Così, ho scritto la seguente query

SELECT t.name 
FROM `incoming_tours` t 
JOIN `incoming_tours_cities` tc0 ON tc0.id_parrent = t.id 
AND tc0.id_city = '4' 
JOIN `incoming_tours_cities` tc1 ON tc1.id_parrent = t.id 
AND tc1.id_city = '74' 

e che funziona bene.

Ma io genera la query dinamicamente e quando il conteggio dei join è grande (circa 15) la query rallenta.

vale a dire quando si tenta di eseguire

SELECT t.name 
FROM `incoming_tours` t 
JOIN `incoming_tours_cities` tc0 ON tc0.id_parrent = t.id 
AND tc0.id_city = '4' 
JOIN `incoming_tours_cities` tc1 ON tc1.id_parrent = t.id 
AND tc1.id_city = '74' 
......................................................... 
JOIN `incoming_tours_cities` tc15 ON tc15.id_parrent = t.id 
AND tc15.id_city = 'some_value' 

la query di esecuzione in 45s (nonostante ho impostato su indici nelle tabelle)

Cosa posso fare, per Optimaze esso?

Grazie mille

+0

ti unisci allo stesso tavolo 14 volte? –

+0

SÌ, perché devo controllare per 14 valori. – Simon

+0

se c'è un altro modo per raggiungere lo stesso effetto, per favore dimmi come questo – Simon

risposta

6
SELECT t.name 
FROM incoming_tours t INNER JOIN 
    (SELECT id_parrent 
    FROM incoming_tours_cities 
    WHERE id IN (4, 74) 
    GROUP BY id_parrent 
    HAVING count(id_city) = 2) resultset 
    ON resultset.id_parrent = t.id 

ma è necessario cambiare il numero delle città conteggio totale.

+1

Prendi solo i parent_ids che hanno abbinato il tuo elenco di città due volte. In questo modo sai che hanno entrambe le città. –

+1

Oh, e sostituisci 'id' con' id_city' –

+0

fammelo testare ... – Simon

0

Solo un suggerimento. Se si utilizza l'operatore IN in una clausola WHERE, è possibile sperare che il cortocircuito dell'operatore AND rimuova gli JOIN s non necessari durante l'esecuzione per i tour che non rispettano il vincolo.

0

sembra un modo strano di fare quella domanda, qui

SELECT t.name FROM `incoming_tours` as t WHERE t.id IN (SELECT id_parrent FROM `incoming_tours_cities` as tc WHERE tc.id_city IN ('4','74')); 

Io penso che lo fa, ma non testato ...

EDIT: Aggiunto tabella alias di sub-query

+0

non funzionerà, perché ho bisogno di ** tutti i ** valori per corrispondere, ma quando hai scritto 'in' è uguale a' o', non ' e 'verifica delle dichiarazioni. – Simon

+0

Ad esempio, dopo aver eseguito la query con valori di ('" 5 "', '" 74 "') otterrete sia 'first_tour' che' second_tour', ma abbiamo bisogno solo del primo. – Simon

+0

Ho eseguito la mia query dopo aver ricreato le tabelle dell'esempio e restituito solo first_tour. – pharalia

1

sono abbastanza sicuro che questo funziona, ma molto meno sicuri che sia ottimale.

SELECT * FROM incoming_tours 
WHERE 
id IN (SELECT id_parrent FROM incoming_tours_cities WHERE id_city=4) 
AND id IN (SELECT id_parrent FROM incoming_tours_cities WHERE id_city=74) 
... 
AND id IN (SELECT id_parrent FROM incoming_tours_cities WHERE id_city=some_value) 
+0

ho lo ha già testato, è più lento rispetto ai join – Simon

+0

Ora ho dato un'occhiata anche ai tempi di esecuzione delle query. Non sembra essere molto più lento a valori più bassi delle condizioni, ma sembra essere molto più veloce avere molte subquery 'SELECT' che avere lo stesso numero di condizioni' JOIN' per un numero maggiore di condizioni. – Aether

2
SELECT name 
FROM (
     SELECT DISTINCT(incoming_tours.name) AS name, 
      COUNT(incoming_tours_cities.id_city) AS c 
     FROM incoming_tours 
      JOIN incoming_tours_cities 
       ON incoming_tours.id=incoming_tours_cities.id_parrent 
     WHERE incoming_tours_cities.id_city IN(4,74) 
      HAVING c=2 
    ) t1; 

Si dovrà cambiare c=2 a tutto ciò il conteggio dei id_city si sta cercando è, ma dal momento che si genera la query in modo dinamico, che non dovrebbe essere un problema.

+0

Non sono sicuro che sia corretto. Non ti serve un 'GROUPBY'? –

+0

Non sembra, l'ho provato e funziona perfettamente. – Narf

0

Ho scritto questa query utilizzando CTE e include i dati di test nella query. Dovrai modificarlo in modo da interrogare le tabelle reali. Non sei sicuro di come funziona su un grande set di dati ...

Declare @numCities int = 2 

;with incoming_tours(id, name) AS 
(
    select 1, 'first_tour' union all 
    select 2, 'second_tour' union all 
    select 3, 'third_tour' union all 
    select 4, 'fourth_tour' 
) 
, incoming_tours_cities(id_parent, id_city) AS 
(
    select 1, 4 union all 
    select 1, 5 union all 
    select 1, 27 union all 
    select 1, 74 union all 
    select 2, 1 union all 
    select 2, 5 
) 
, cityIds(id_city) AS 
( 
    select 4 
    union all select 5 
    /* Add all city ids you need to check in this table */ 
) 
, common_cities(id_city, tour_id, tour_name) AS 
(
    select c.id_city, it.id, it.name 
    from cityIds C, Incoming_tours_cities tc, incoming_tours it 
    where C.id_city = tc.id_city 
    and tc.id_parent = it.id 
) 
, tours_with_all_cities(id_city) As 
(
    select tour_id from common_cities 
    group by tour_id 
    having COUNT(id_city) = @numCities 
) 
select it.name from incoming_tours it, tours_with_all_cities tic 
where it.id = tic.id_city 
Problemi correlati