【问题标题】:Ordering SQL results by attributes in another table按另一个表中的属性对 SQL 结果进行排序
【发布时间】:2016-01-14 15:04:06
【问题描述】:

如果我有一个包含多个停靠点的巴士表,并且每个停靠点记录都有一个到达时间,我如何按最早的停靠点时间检索和订购巴士?

 _______    ________
| Buses |  | Stops  |
|-------|  |--------|
| id    |  | id     |
| name  |  | bus_id |
 -------   | time   |
            --------

我可以通过以下查询来做到这一点:

SELECT DISTINCT sub.id, sub.name
FROM
(SELECT buses.*, stops.time
 FROM buses
 INNER JOIN stops ON stops.bus_id = buses.id
 ORDER BY stops.time) AS sub;

...但是这样做的缺点是必须执行 2 次查询,并且必须在 SELECT DISTINCT 子句中指定总线中的所有字段。如果 bus 表发生变化,这会变得特别烦人。

我想做的是这样的:

SELECT DISTINCT buses.*
FROM buses
INNER JOIN stops ON stops.bus_id = buses.id
ORDER BY stops.time;

...但是,为了获得DISTINCT buses.*,我还必须在其中包含stops.time,这会给我提供不同停靠时间的重复巴士。

执行此查询的更好方法是什么?

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    您可以做的一件事是将内部查询放入ORDER BY。这将保持外部查询“干净”,因为它只会从总线中选择。这样您就不需要返回任何额外的字段。

    SELECT buses.*
    FROM buses
    ORDER BY (
      SELECT MIN(stops.time) FROM stops WHERE stops.bus_id = buses.id
    )
    

    【讨论】:

    • 这是最简单的答案,从维护的角度来看可能是最好的。谢谢!
    【解决方案2】:

    在 select 中指定字段是最佳做法,所以我不确定为什么将其列为缺点。

    我愿意

    Select buses.* From buses inner join
    (Select stops.bus_id, min(stops.time) as mintime 
        From Stops 
        Group By stops.bus_id) st on buses.id = st.bus_id
    

    Select buses.*, min(stops.time) as stoptime 
        From buses inner join stops on 
        buses.ID = stops.bus_ID group by buses.id, buses.name
    

    【讨论】:

      【解决方案3】:

      我没有 postgresql,但我认为这个问题中的问题对于任何 SQL 来说都是普遍的 问题是 DISTINCT 永远不会给出正确的解决方案,因为表 Stops 中的所有停止时间都会被列出。

      为了得到正确的解决方案,我们可以使用 GROUP BY 和 MIN 代替..

      SELECT B.id, B.name, MIN(S.time) MinTime
      FROM
      Buses B INNER JOIN Stops S
      ON B.id = S.bus_id
      GROUP BY B.id, B.name
      ORDER BY MIN(S.time)
      

      【讨论】:

        【解决方案4】:

        我想以“选择不同”的说法作为开场白,这种说法被过度使用并且通常很难理解。这是一个非常有限的工具,如果您已经使用过它并且查询仍然无法满足您的需求,那么接下来的 2 个工具是 1。分组依据2。 ROW_NUMBER()

        在这个不需要太多列的示例中,因此 GROUP BY 将很容易产生想要的结果:

        SELECT
              B.id
            , B.name
            , MIN(S.time) MinTime
        FROM Buses B
              INNER JOIN Stops S ON B.id = S.bus_id
        GROUP BY
              B.id
            , B.name
        ORDER BY
              MinTime
        
        • nb:这与 Susilo 的回答完全相同,只是我使用过 order by 子句中的列别名 MinTIme

        一种有效的替代方法,当您需要某一行中的所有列时很有用,例如表示“最早停止时间”的行是使用 ROW_NUMBER()

        SELECT
              id
            , name
            , time
        FROM (
              SELECT
                    B.id
                  , B.name
                  , S.time
                  , ROW_NUMBER() OVER (PARTITION BY B.id ORDER BY S.time ASC) AS rowno
              FROM Buses B
                    INNER JOIN Stops S ON B.id = S.bus_id
              ) dt
        WHERE rowno = 1
        ORDER BY
              Time
        ;
        
        • 提示:如果您在查询要求中看到/听到“最早”或“最新” 那么这种技术经常适用。

        【讨论】:

          猜你喜欢
          • 2013-08-09
          • 1970-01-01
          • 2019-03-23
          • 1970-01-01
          • 1970-01-01
          • 2021-04-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多