1

我的数据库中有 2 个表:BusLines& BusStops。的每个实例都BusLines可以按特定顺序有许多与之关联的停靠点。为了使关联更易于管理(在现有行中删除或添加新停靠点),关联表设计为具有以下结构:

id_BusLine | id_BusStop | id_NextBusStop | isFirstStop

这样做似乎是个好主意,而不是给每个停靠点一个数字,一旦必须将新停靠点添加到行的开头,就必须为每条记录更改该数字(但如果你有更好的主意,我肯定很想听)。

因此,重申一下:我如何在每一行上创建一个 SELECT 语句以使所有停靠点都按正确的顺序排列?因为我不能用简单的 ORDER BY 来解决它...

4

5 回答 5

1

如果使用 sql server,那么我认为这样的递归 cte 有效

;WITH route AS
(
  SELECT BusLineId, BusStopId, NextBusStopId
  FROM BusLine_BusStop
  WHERE IsFirstStop = 1
  UNION ALL
  SELECT b.BusLineId, b.BusStopId, b.NextBusStopId
  FROM BusLine_BusStop b
  INNER JOIN route r
          ON r.BusLineId = b.BusLineId
         AND r.NextBusStopId = b.BusStopId
  WHERE IsFirstStop = 0
)

SELECT BusLineId, BusStopId
FROM route
ORDER BY BusLineId

演示

于 2013-06-01T16:27:59.930 回答
0

适用于 SQL Server 2008-2012、PostgreSQL 9.1.9、Oracle 11g 的解决方案

实际上,递归 CTE 是几乎所有当前 RDBMS 的解决方案,包括 PostgreSQL(解释和示例如下所示)。但是,对于 Oracle DB,还有另一种更好的解决方案(优化):分层查询。

NOCYCLE 指示 Oracle 返回行,即使您的数据中有循环。

CONNECT_BY_ROOT 使您可以访问根元素,甚至是查询中的几层。

使用 HR 模式:

Oracle 11g 对应的代码:

select
b.id_bus_line, b.id_bus_stop
from BusLine_BusStop b
start with b.is_first_stop = 1
connect by nocycle prior b.id_next_bus_stop = b.id_bus_stop and prior b.id_bus_line = b.id_bus_line

Oracle 11g 的演示(我自己的代码)。

请注意,标准是 SQL:1999 规范中的递归 CTE。如您所见,SQL Server 和 PostgreSQL 之间存在一些差异。

以下解决方案适用于 SQL Server 2012:

;WITH route AS
(
  SELECT BusLineId, BusStopId, NextBusStopId
  FROM BusLine_BusStop
  WHERE IsFirstStop = 1
  UNION ALL
  SELECT b.BusLineId, b.BusStopId, b.NextBusStopId
  FROM BusLine_BusStop b
  INNER JOIN route r
          ON r.BusLineId = b.BusLineId
         AND r.NextBusStopId = b.BusStopId
  WHERE IsFirstStop = 0 or IsFirstStop is null
)
SELECT BusLineId, BusStopId
FROM route
ORDER BY BusLineId

SQL Server 2012 的演示(受 TI 启发)。

这个适用于 PostgreSQL 9.1.9(它不是最佳的,但应该可以工作):

诀窍在于为您可以重置的当前会话创建一个专用的临时序列。

create temp sequence rownum;

WITH final_route AS
(
  WITH RECURSIVE route AS
  (
    SELECT BusLineId, BusStopId, NextBusStopId
    FROM BusLine_BusStop
    WHERE IsFirstStop = 1
    UNION ALL
    SELECT b.BusLineId, b.BusStopId, b.NextBusStopId
    FROM BusLine_BusStop b
    INNER JOIN route r
            ON r.BusLineId = b.BusLineId
           AND r.NextBusStopId = b.BusStopId
    WHERE IsFirstStop = 0 or IsFirstStop is null
  )
  SELECT BusLineId, BusStopId, nextval('rownum') as rownum
  FROM route
)
SELECT BusLineId, BusStopId
FROM final_route
ORDER BY BusLineId, rownum;

我自己的 PostgreSQL 9.1.9 的演示。

编辑:

很抱歉进行了多次编辑。通过子记录而不是其父记录连接记录是非常罕见的。您可以通过删除 isFirstStop 列并使用 id_PreviousBusStop 列(如果可能)连接您的记录来避免这种糟糕的表示。在这种情况下,您必须将第一条记录的 id_PreviousBusStop 设置为 null。您可以节省空间(对于固定长度的数据,仍然保留整个空间)。此外,您的查询将使用更少的字符变得更有效率。

于 2013-06-01T16:41:09.063 回答
0

当前设计

假设这张表

CREATE TEMP TABLE bus_lnk(
  id_busline     integer
 ,id_busstop     integer
 ,id_nextbusstop integer
 ,isfirststop    boolean
);

此查询包括每行每个站点的运行编号,并且应该比迄今为止发布的更简单和更快:

WITH RECURSIVE r AS (
   SELECT id_busline, id_busstop, id_nextbusstop, 1 AS stop_nr
   FROM   bus_lnk
   WHERE  isfirststop               -- properly testing boolean column in pg

   UNION  ALL
   SELECT id_busline, b.id_busstop, b.id_nextbusstop, r.stop_nr + 1
   FROM   bus_lnk b
   JOIN   r USING (id_busline)      -- simple join
   WHERE  r.id_nextbusstop = b.id_busstop
   AND    isfirststop IS NOT TRUE   -- simple, proper test
   )
SELECT id_busline, id_busstop, stop_nr
FROM   r
ORDER  BY id_busline, stop_nr;

(SQLfiddle 已关闭 ATM。)

更简单的设计

由于公交线路每天都不会改变,我建议使用更简单的表格布局:

CREATE TEMP TABLE bus_lnk(
  id_busline     integer
 ,id_busstop     integer
 ,stop_rnk       integer 
);

您可以stop_rnk以 1000 步(或其他)开始您的数字。如果发生变化,数字空间中有足够的空间容纳更多站点。要获得一个没有间隙的干净编号,请运行相对简单的查询:

SELECT id_busline, id_busstop
      ,row_number() OVER (PARTITION BY id_busline ORDER BY stop_rnk) AS stop_nr
FROM   bus_lnk
ORDER  BY id_busline, stop_rnk;
于 2013-06-02T18:12:22.887 回答
0

这听起来像是一种自加入的情况,例如:

SELECT * 
FROM table a
JOIN table b
   ON  a.id_BusLine = b.id_Busline
   AND a.id_NextBusStop = b.id_BusStop

这将它们链接在一起,但是如果您的 isFirstStop 为 1/0,那么您也需要订购:

SELECT * 
FROM table a
JOIN table b
   ON  a.id_BusLine = b.id_Busline
   AND a.id_NextBusStop = b.id_BusStop
ORDER BY isFirstStop, ....

之后需要一些方法来订购停靠点。

于 2013-06-01T16:18:34.180 回答
0

精细 SQL 方法的替代方法是复制以 10 为单位指定行号的旧 BASIC 技巧,这样您仍然可以在现有行号之间插入新行号——如果您需要坚持整数。

当然,如果您愿意,您可以以 1,000 为增量进行编号。如果你喜欢干净整洁的东西,你可以定期运行重新编号例程以使数字均匀。

技术含量低,可以在任何数据库上工作,简单的 order-by 将优于所有其他方法。

于 2013-06-01T19:28:02.883 回答