需要帮助优化查询

时间:2010-10-21 08:19:40

标签: php mysql join phpmyadmin performance

我有两张桌子 - 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")

AND second_tour包含城市列表 - ("1","5")


我们假设我有两个值 - 474

现在,我需要从第一个表中获取所有行,其中两个值都在城市列表中。即它必须只返回 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中的查询运行(尽管我在表中设置了索引)

我能做些什么来优化它?

非常感谢

6 个答案:

答案 0 :(得分: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 :(得分: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计数,但由于您动态生成查询,这应该不是问题。

答案 2 :(得分: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)

答案 3 :(得分:0)

只是一个暗示。 如果您在IN子句中使用WHERE运算符,则可以希望运算符AND的短路可以在执行期间删除不必要的JOIN不尊重约束。

答案 4 :(得分: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'));

认为这样做,但未经过测试......

编辑:为子查询添加了表别名

答案 5 :(得分: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