2010-10-21 3 views
5

У меня есть две таблицы - incoming tours(id,name) и incoming_tours_cities(id_parrent, id_city)Нужна помощь в оптимизации запроса

id в первой таблице является уникальным, и для каждой уникальной строки из первой таблицы есть список id_city - s во второй таблице (т.е. id_parrent в вторая таблица равна id из первой таблицы)

Например

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

Это означает, что first_tour имеет список городов - ("4","5","27","74")

И second_tour имеет список городов - ("1","5")


Давайте предположим, что у меня есть два значения - 4 и 74:

Теперь мне нужно получить все строки из первой таблицы, где мои значения указаны в списке городов. то есть он должен вернуть только first_tour (потому что 4 и 74 в этом список городов)

Итак, я написал следующий запрос

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' 

И это прекрасно работает.

Но я генерирую запрос динамически, и когда количество объединений велико (около 15), запрос замедляется.

т.е. когда я пытаюсь запустить

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' 

запроса протекание в 45s (несмотря на я установить индексы в таблицах)

Что я могу сделать, чтобы optimaze это?

Большое спасибо

+0

Вы присоединяетесь к одной и той же таблице 14 раз? –

+0

ДА, потому что я должен проверить 14 значений. – Simon

+0

Если есть другой способ достичь такого же эффекта, скажите, пожалуйста, как – Simon

ответ

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 

Но вам нужно изменить количество Графа всего города.

+1

Вы берете только parent_ids, которые дважды соответствуют вашему списку городов. Таким образом, вы знаете, что у них есть оба города. –

+1

О, и замените 'id' на' id_city' –

+0

позвольте мне проверить его ... – Simon

0

Просто подсказка. Если вы используете оператор IN в предложении WHERE, вы можете надеяться, что короткое замыкание оператора AND может удалить ненужные JOIN с во время выполнения для туров, которые не соблюдают ограничение.

0

Кажется странным образом сделать этот запрос, здесь

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

Я думаю, что делает это, но не проверял ...

EDIT: Добавлена ​​таблица псевдонимов для подзапроса

+0

это не сработает, потому что мне нужно ** все ** значения, чтобы соответствовать, но когда вы написали 'in', это равно' или', а не ' и 'утверждение оператора. – Simon

+0

т. Е. После выполнения запроса с значениями ('' 5 "', '" 74 "') вы получите как 'first_tour', так и' second_tour', но нам нужно только первое. – Simon

+0

Я выполнил свой запрос после повторного создания таблиц из примера, и он возвращает только first_tour. – pharalia

1

Я уверен, что это работает, но намного меньше, уверен, что это является оптимальным.

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

протестировал его уже, он медленнее, чем при подключении – Simon

+0

Теперь я посмотрел также время выполнения запроса. Кажется, что это намного медленнее при более низких значениях условий, но, похоже, гораздо быстрее иметь много подзапросов SELECT, чем иметь такое же количество условий 'JOIN' для большего количества условий. – 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; 

Вы должны изменить c=2 к тому, что отсчет id_city вы ищете это, но так как вы генерировать запрос динамически, что не должно быть проблемой.

+0

Я не уверен, что это правильно. Вам не нужен «GROUPBY»? –

+0

Не похоже, что я тестировал, и все работает отлично. – Narf

0

Я написал этот запрос, используя CTE, и включает в себя тестовые данные в запросе. Вам нужно будет изменить его, чтобы вместо этого запрашивать реальные таблицы. Не знаете, как это работает на большом наборе данных ...

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 
Смежные вопросы