【问题标题】:Sorting an SQL table with rows that relate to each other对包含彼此相关的行的 SQL 表进行排序
【发布时间】:2026-02-23 07:15:01
【问题描述】:

我有一个包含假日巴士旅行时间的 SQL 表。该表结合了去程和回程(选项0是去,选项1是返回),它还为用户提供了多种选择(选项2计算选项:3个去程和2个回程)。每次行程可能跨越多行,因为该表列出了停靠点之间的所有内容:

  • 去:

    • 选项 0:伦敦 -> 阿姆斯特丹 -> 柏林
    • 选项 1:伦敦 -> 苏黎世 -> 柏林
    • 选项 2:伦敦 -> 巴黎 -> 罗马 -> 柏林
  • 返回

    • 选项 0:柏林 -> 阿姆斯特丹 -> 伦敦
    • 选项 1:柏林 -> 苏黎世 -> 伦敦

选项列显示旅行是去还是返回。 Option2 列将选项匹配在一起。 Option3 列显示了每个选项的正确顺序。

+----+---------------------+---------------------+------------------+----------------+--------------+---------------+---------------+
| ID | DepartureDateTime   | ArrivalDateTime     | Departure        | Arrival        | Option       | Option2       | Option3       |
+----+---------------------+---------------------+------------------+----------------+--------------+---------------+---------------+
| 72 | 2013-10-01 13:45:00 | 2013-10-02 16:40:00 | London           | Amsterdam      |            0 |             0 |             0 |
| 73 | 2013-10-02 17:35:00 | 2013-10-03 19:05:00 | Amsterdam        | Berlin         |            0 |             0 |             1 |
| 74 | 2013-10-01 17:00:00 | 2013-10-02 19:50:00 | London           | Zurich         |            0 |             1 |             0 |
| 75 | 2013-10-02 21:10:00 | 2013-10-03 22:40:00 | Zurich           | Berlin         |            0 |             1 |             1 |
| 76 | 2013-10-01 06:00:00 | 2013-10-02 08:40:00 | London           | Paris          |            0 |             2 |             0 |
| 77 | 2013-10-02 12:30:00 | 2013-10-03 14:05:00 | Paris            | Rome           |            0 |             2 |             1 |
| 78 | 2013-10-03 12:30:00 | 2013-10-04 14:05:00 | Rome             | Berlin         |            0 |             2 |             2 |
| 79 | 2013-10-10 14:50:00 | 2013-10-11 16:30:00 | Berlin           | Amsterdam      |            1 |             0 |             0 |
| 80 | 2013-10-11 17:05:00 | 2013-10-12 17:50:00 | Amsterdam        | London         |            1 |             0 |             1 |
| 81 | 2013-10-10 06:45:00 | 2013-10-11 08:25:00 | Berlin           | Zurich         |            1 |             1 |             0 |
| 82 | 2013-10-11 15:20:00 | 2013-10-12 16:05:00 | Zurich           | London         |            1 |             1 |             1 |
+----+---------------------+---------------------+------------------+----------------+--------------+---------------+---------------+

我想要两个不同的查询:

1) 根据两件事对表格进行排序: 一种。外出:旅行的初始出发(离开伦敦),不弄乱后续站点的顺序。 湾。返程:最后一次返程(去伦敦)的到达,再次不打乱后续站点的顺序。

2) 仅返回符合特定日期/时间范围的行程:初始出发(离开伦敦)和最终返回(进入伦敦)。例如,显示早上出发和晚上到达的行程。

如果您需要更多详细信息或我遗漏了什么,请告诉我。

提前感谢您的帮助。

编辑 1

请阅读我的整个帖子。这里重要的是行是相互关联的。例如,下面的两行必须“一起”,我正在处理的应用程序取决于正确的顺序:

+----+---------------------+---------------------+------------------+----------------+--------------+---------------+---------------+
| ID | DepartureDateTime   | ArrivalDateTime     | Departure        | Arrival        | Option       | Option2       | Option3       |
+----+---------------------+---------------------+------------------+----------------+--------------+---------------+---------------+
| 72 | 2013-10-01 13:45:00 | 2013-10-02 16:40:00 | London           | Amsterdam      |            0 |             0 |             0 |
| 73 | 2013-10-02 17:35:00 | 2013-10-03 19:05:00 | Amsterdam        | Berlin         |            0 |             0 |             1 |

也就是说,不能按出发日期排序,因为行会混淆。

所以,如果我想根据出发时间对上述行程进行排序,首先会出现伦敦到柏林经巴黎的行程,因为它在早上 6 点出发:

+----+---------------------+---------------------+------------------+----------------+--------------+---------------+---------------+
| ID | DepartureDateTime   | ArrivalDateTime     | Departure        | Arrival        | Option       | Option2       | Option3       |
+----+---------------------+---------------------+------------------+----------------+--------------+---------------+---------------+
| 76 | 2013-10-01 06:00:00 | 2013-10-02 08:40:00 | London           | Paris          |            0 |             2 |             0 |
| 77 | 2013-10-02 12:30:00 | 2013-10-03 14:05:00 | Paris            | Rome           |            0 |             2 |             1 |
| 78 | 2013-10-03 12:30:00 | 2013-10-04 14:05:00 | Rome             | Berlin         |            0 |             2 |             2 |
| 72 | 2013-10-01 13:45:00 | 2013-10-02 16:40:00 | London           | Amsterdam      |            0 |             0 |             0 |
| 73 | 2013-10-02 17:35:00 | 2013-10-03 19:05:00 | Amsterdam        | Berlin         |            0 |             0 |             1 |

上面的部分表显示了排序结果的样子。基本上,排序算法应该考虑初始出发的行并忽略排序中的其他行,但最终结果应该是在初始行程“下方”的行程中的相关停靠点。

这听起来很可怕还是什么?

任何帮助将不胜感激。

编辑 2

根据要求,我使用的是 MySQL 5.1。

编辑 3

@fancyPants 成员解决了第一个问题。考虑到从 Option=0 到 Option=1 的变化,我做了一点修改:

SELECT 
`ID`, `DepartureDateTime`, `ArrivalDateTime`, `Departure`, `Arrival`, `Option`, `Option2`, `Option3`
FROM (
SELECT 
t.*,
CASE WHEN Option != @prev OR Option2 != @prev2 THEN @min_date := DepartureDateTime ELSE @min_date END as min_date,
CASE WHEN Option2 = @prev2 THEN @counter := @counter + 1 ELSE @counter := 0 END as counter,
@prev := Option, @prev2 := Option2
FROM Table1 t 
, (SELECT @min_date:=(SELECT DepartureDateTime FROM Table1 ORDER BY `Option`, Option2, Option3 LIMIT 1), @counter:=0, @prev:=NULL, @prev2:=NULL) vars
order by `Option`, Option2, Option3
) sq
ORDER BY min_date, counter

谢谢fancyPants,很棒的工作!

很遗憾,我对第二个查询不够清楚。我需要的是建立在第一个查询之上(从而对结果进行排序),然后根据日期时间范围限制结果。

【问题讨论】:

  • 向我们展示您的尝试以及您遇到的问题,我们会为您提供帮助。
  • 你使用的是什么数据库系统?
  • 我改进了对问题的描述。这是基于现有的应用程序(旧的,不是我制作的),所以这是我必须使用的。对不起,伙计们。
  • 有一些特性可以使编写这个查询变得容易,但它们并不是在所有 SQL 数据库系统中都可用 - 所以如果你可以为你的特定数据库系统(例如 SQL Server、Oracle , MySQL),我们也许可以适当地调整结果。
  • 应该可以使用支持窗口函数的 DBMS(lead()lag()

标签: mysql sql sorting


【解决方案1】:

这不是那么容易,这是我想出的(假设是 MySQL):

根据两件事对表格进行排序:a。外出:行程的初始出发(离开伦敦),不打乱后续站点的顺序:

SELECT 
`ID`, `DepartureDateTime`, `ArrivalDateTime`, `Departure`, `Arrival`, `Option`, `Option2`, `Option3`
FROM (
SELECT 
t.*,
CASE WHEN Option2 != @prev THEN @min_date := DepartureDateTime ELSE @min_date END as min_date,
CASE WHEN Option2 = @prev THEN @counter := @counter + 1 ELSE @counter := 0 END as counter,
@prev := Option2
FROM Table1 t 
, (SELECT @min_date:=(SELECT DepartureDateTime FROM Table1 ORDER BY `Option`, Option2, Option3 LIMIT 1), @counter:=0, @prev:=NULL) vars
order by `Option`, Option2, Option3
) sq
ORDER BY min_date, counter

返回:

SELECT 
`ID`, `DepartureDateTime`, `ArrivalDateTime`, `Departure`, `Arrival`, `Option`, `Option2`, `Option3`
FROM (
SELECT 
t.*,
CASE WHEN Option2 != @prev THEN @min_date := ArrivalDateTime ELSE @min_date END as min_date,
CASE WHEN Option2 = @prev THEN @counter := @counter + 1 ELSE @counter := 0 END as counter,
@prev := Option2
FROM Table1 t 
, (SELECT @min_date:=(SELECT ArrivalDateTime FROM Table1 ORDER BY `Option`, Option2, Option3 LIMIT 1), @counter:=0, @prev:=NULL) vars
order by `Option`, Option2, Option3
) sq
ORDER BY min_date, counter

对于你的第二个问题,如果我理解正确,你想要这样的东西:

SELECT 
t1.DepartureDateTime AS t1dep,
t2.ArrivalDateTime AS t2arr
, t1.*, t2.*
FROM Table1 t1
INNER JOIN Table1 t2 ON t1.Option = t2.Option AND t1.Option2 = t2.Option2 
WHERE t1.Option3 = (SELECT MIN(Option3) FROM Table1 t3 WHERE t1.Option = t3.Option AND t1.Option2 = t3.Option2)
AND t2.Option3 = (SELECT MAX(Option3) FROM Table1 t3 WHERE t1.Option = t3.Option AND t1.Option2 = t3.Option2)

AND t1.DepartureDateTime BETWEEN '2013-10-01 05:00:00' AND '2013-10-01 07:00:00'
AND t2.ArrivalDateTime BETWEEN '2013-10-04 14:00:00' AND '2013-10-04 15:00:00'

此查询返回最小的出发日期时间,即行程第一站的出发日期和一条线路中最后一站的到达日期。然后你可以简单地调整 where 子句。

编辑:你在寻找这样的东西吗?

SELECT 
l.* FROM
(
SELECT 
`ID`, `DepartureDateTime`, `ArrivalDateTime`, `Departure`, `Arrival`, `Option`, `Option2`, `Option3`
,min_date, counter 
FROM (
SELECT 
t.*,
CASE WHEN `Option` != @prev OR Option2 != @prev2 THEN @min_date := DepartureDateTime ELSE @min_date END as min_date,
CASE WHEN Option2 = @prev2 THEN @counter := @counter + 1 ELSE @counter := 0 END as counter,
@prev := `Option`, @prev2 := Option2
FROM Table1 t 
, (SELECT @min_date:=(SELECT DepartureDateTime FROM Table1 ORDER BY `Option`, Option2, Option3 LIMIT 1), @counter:=0, @prev:=NULL, @prev2:=NULL) vars
order by `Option`, Option2, Option3
) sq
) l 
INNER JOIN
(SELECT `Option`, Option2 FROM Table1 WHERE DepartureDateTime BETWEEN '2013-10-02 11:30:00' AND '2013-10-02 13:00:00'
                          OR ArrivalDateTime BETWEEN '2013-10-03 14:00:00' AND '2013-10-03 14:15:00'
) r
ON l.`Option` = r.`Option` AND l.Option2 = r.Option2
ORDER BY min_date, counter

【讨论】:

  • 出色的工作,谢谢!我的问题的第一部分已经解决(在我原来的帖子中,我添加了您的解决方案并略有改进)。不幸的是,我的问题的第二部分(根据日期范围限制结果)并不完整,因为我需要它基于第一个查询(排序结果)。再次感谢您!
  • @Jimbo 编辑了我的答案。我认为有问题 2 的解决方案。顺便说一句,在 * 上表示感谢的常用方法是投票和/或接受答案;)无论如何,很高兴听到我已经可以帮助你。
  • 我已接受并赞成您的回答。真正精湛的工作。有句话叫“给饥饿的人一条鱼,他会吃一天,教他如何钓鱼,他会吃一辈子”。换句话说,我正在尝试逐行了解您的查询是如何工作的(我是一名 PHP 开发人员,对 MySQL 的了解有限)。似乎我了解大部分概念(子查询、别名、内部连接),但似乎我对这些“@”变量和“CASE WHEN”一无所知。看来我需要多读书了。再次感谢您!
【解决方案2】:

对于第一个问题 - 进行查询

select * from time_table
where option = 0
order by DepartureDateTime, Option2, Option3;

第一个问题 - 返回查询

select * from time_table
where option = 1
order by ArrivalDateTime, Option2, Option3;

此结果基于了解您的要求。它不是很清楚。

请为第二个问题添加一些详细信息。无法理解这个问题。 用数据举例。

【讨论】:

  • 抱歉给您带来了困惑,感谢您的帮助。我添加了更多关于我的问题的内容并添加了一些示例。这里的问题是行是相互关联的,行程不能被打破:伦敦->阿姆斯特丹->柏林应该一个接一个地出现。
  • @ChamlyIdunil 当 OP 声明您的答案错误时,请删除或编辑它。它的存在可能会阻止其他人甚至看这个问题。