2015-08-18 40 views
0

我正在开发一个应用程序(使用CodeIgniter),需要使用相当复杂的查询检索行程。下面是它的一个例子:MySQL连接在复杂查询中丢失

SELECT *, CONCAT(`u1`.`city`,', ', `u1`.`country_id`) as `from_city`, 
      CONCAT(`u2`.`city`,', ', `u2`.`country_id`) as `to_city` 
FROM trips 
LEFT JOIN `cities` as `u1` ON (`u1`.`id`=`from_city`) 
LEFT JOIN `cities` as `u2` ON (`u2`.`id`=`to_city`) 
WHERE trip_id 
IN (SELECT trip_id 
    FROM stops 
    WHERE city_id = 7583 
    AND trip_id 
    IN (SELECT trip_id 
     FROM stops 
     WHERE trip_id 
     IN (SELECT trip_id 
      FROM stops 
      WHERE city_id=7565))) 
      ORDER BY departure_date; 

它曾经工作得很好,直到今天。它涉及3个表格(旅行,停靠和城市)。自开始测试以来,出行和停靠表格的增长并不是很大,可能从200到300条记录。

如果我在CodeIgniter应用程序中测试,我得到一个2006年或2013年'失去连接到MySQL服务器...'错误。如果我使用NaviCat进行查询,则会得到与“在查询期间丢失与MySQL服务器的连接”消息。

为了更好地说明什么,我想在这里完成的是表什么样子:

旅行(未显示所有列)

trips table

停止

all columns shown

城市(并非所有列都显示)

cities table

基本上货轮插入行程并详细说明它们的停靠位置,然后代理商会在可用行程中搜索。因此,如果货机发布行程为A-> B-> C-> D-> E,则另一货机发布行程为K-> H-> E-> D-> C,当代理商寻找C-> E。 stop表中的'ordinal'列是我后来筛选错误方向的行程的方式。

什么可以延长响应时间呢?我如何重写这个查询来使它更快?

+2

最有可能这个查询超时......为什么在这个世界上你在做一个三重嵌套'in'子句?!这是**可怕的**效率低下。 – mituw16

+1

在WHERE中嵌套的3个级别可能会导致性能下降。请发布'travelling,cities'表中的行样本,以及预期的查询输出结果的样本。 –

+1

尽可能地尝试加入。嵌套的'IN'子查询是非常低效的。进行匹配时,IN子查询将在每一行上运行。 – khuderm

回答

1

如果我理解您的查询的权利,它看起来就像你试图检索两市7583和7565.

之间的所有旅行如果是下面的查询应返回相同的结果,并多一点效率:

SELECT *, CONCAT(u1.city,', ', u1.country_id) as from_city, 
      CONCAT(u2.city,', ', u2.country_id) as to_city 
FROM trips t1 
LEFT JOIN cities as u1 ON (u1.id=from_city) 
LEFT JOIN cities as u2 ON (u2.id=to_city) 
JOIN (
    SELECT trip_id FROM stops t1 
    WHERE city_id IN (7583, 7565) 
    GROUP BY trip_id 
    HAVING COUNT(DISTINCT city_id) = 2 
) x ON t1.trip_id = x.trip_id 
ORDER BY departure_date; 

或者,也许我完全误解了你在做什么,在这种情况下,我会删除这个答案。

+1

谢谢!你说得对,这个查询要快得多。即使知道它存在,我也从未使用HAVING。我会用更多的练习来让我的工具更加锐利。 – zJorge

+0

@zJorge用适当的键(和索引)设置这应该表现良好。 – jpw

0

我会说,你可以在你的查询重新写入

SELECT 
    *, 
    CONCAT(`u1`.`city`,', ', `u1`.`country_id`) as `from_city`, 
    CONCAT(`u2`.`city`,', ', `u2`.`country_id`) as `to_city` 
FROM trips 
LEFT JOIN `cities` as `u1` ON (`u1`.`id`=`from_city`) 
LEFT JOIN `cities` as `u2` ON (`u2`.`id`=`to_city`) 
INNER JOIN stops as `stops` ON trips.trip_id = stops.trip_id 
WHERE stops.city_id IN (7583, 7565) -- all stops in this 2 cities 
; 

这应该跑得挺快十岁上下,如果你可以使用适当的索引...

+1

您可能需要添加COUNT和HAVING thatCount = 2,因为原始任务似乎希望在两个city_id值中都“停止”。 – Uueerdo

+0

感谢@Benvorth,但此查询正在返回第一个或第二个城市的旅行。结果应该只包括两次停车的行程。 – zJorge