2010-10-21 11 views
5

Tengo dos tablas - incoming tours(id,name) y incoming_tours_cities(id_parrent, id_city)necesita ayuda en la optimización de consulta

id en primera tabla es única, y para cada fila única desde primera tabla se encuentra la lista de id_city - s en la segunda tabla (es decir id_parrent en segunda tabla es igual a id de primera tabla)

Por ejemplo

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-----| 
........................ 

Eso significa que first_tour lista de ciudades tiene - ("4","5","27","74")

Y second_tour tiene lista de ciudades - ("1","5")


Supongamos que tengo dos valores - 4 y 74:

Ahora, necesito obtener todas las filas de la primera tabla, donde mis ambos valores están en la lista de ciudades. es decir que debe devolver sólo el first_tour (porque 4 y 74 se encuentran en su lista de ciudades)

Así, escribí la siguiente consulta

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' 

y que funciona bien.

Pero genero la consulta dinámicamente, y cuando el recuento de uniones es grande (alrededor de 15) la consulta se ralentiza.

es decir, cuando trato de correr a correr

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 consulta en 45s (a pesar de que puse índices en las tablas)

¿Qué puedo hacer yo, para que Optimaze?

Muchas gracias

+0

¿se une a la misma tabla 14 veces? –

+0

SÍ, porque tengo que verificar 14 valores. – Simon

+0

si hay otra forma de alcanzar el mismo efecto, por favor dígame cómo – Simon

Respuesta

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 

pero hay que cambiar el número de ciudades en total cuentan.

+1

Solo toma los parent_ids que coinciden con su lista de ciudades dos veces. De esa forma sabes que tienen ambas ciudades. –

+1

Ah, y reemplace 'id' con' id_city' –

+0

déjeme probarlo ... – Simon

0

Sólo una sugerencia. Si usa el operador IN en una cláusula WHERE, puede esperar que el cortocircuito del operador AND elimine innecesarios JOIN durante la ejecución de los recorridos que no respetan la restricción.

0

parece como una extraña manera de hacer esa consulta, aquí

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')); 

Yo creo que lo hace, pero no a prueba ...

EDIT: Añadido a la tabla de alias sub-consulta

+0

esto no funcionará, porque necesito ** todos los valores ** para que coincida, pero cuando escribió 'in' es igual a' o', no ' y 'verificación de declaración. – Simon

+0

es decir, después de ejecutar su consulta con los valores de ('" 5 "', '" 74 "') obtendrá tanto 'first_tour' como' second_tour', pero necesitamos el primero solamente. – Simon

+0

Ejecuté mi consulta después de volver a crear las tablas del ejemplo, y solo devuelve first_tour. – pharalia

1

estoy bastante seguro de que esto funciona, pero mucho menos seguro que es óptima.

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

tengo ya lo probé, es más lento que con las uniones – Simon

+0

Ahora también he echado un vistazo a los tiempos de ejecución de las consultas. No parece ser mucho más lento en valores más bajos de condiciones, pero parece ser mucho más rápido tener muchas subconsultas 'SELECT' que tener el mismo número de condiciones 'JOIN' para un mayor número de condiciones. – 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; 

Usted tendrá que cambiar c=2 a lo que el recuento de id_city que está buscando es, pero desde que se genera la consulta dinámica, que no debería ser un problema.

+0

No estoy seguro de que esto sea correcto. ¿No necesitas un 'GROUPBY'? –

+0

Parece que no, lo probé y funciona bien. – Narf

0

He escrito esta consulta utilizando CTE e incluye los datos de prueba en la consulta. Tendrá que modificarlo para que consulte las tablas reales en su lugar. No estoy seguro de cómo funciona en un gran conjunto de datos ...

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 
Cuestiones relacionadas