当每行包含下一行的id时,命令SQL请求

时间:2013-06-01 16:03:26

标签: sql postgresql request

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

id_BusLine | id_BusStop | id_NextBusStop | isFirstStop

这样做似乎是一个好主意,而不是给每个停止一个数字,一旦新的停止必须添加到行的开头,必须为每个记录更改(但如果你有一个更好的主意,我当然希望听到它。)

所以,重申一下:如何在每一行上创建一个SELECT语句,以便以正确的顺序拥有所有停靠点?因为我只用一个简单的ORDER BY来解决它......

5 个答案:

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

demo

答案 1 :(得分: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, ....

在此之后需要某种方式来订购止损。

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

DEMO for 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

DEMO for SQL Server 2012(受到T I的启发)。

这个适用于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;

DEMO for PostgreSQL 9.1.9我自己的。

修改

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

答案 3 :(得分:0)

精细SQL方法的另一种选择是复制旧的BASIC技巧,以10为单位指定行号,这样你仍然可以在现有的那些之间插入新的 - 如果你需要坚持那些整数。

当然,如果你愿意,你可以以1,000为增量编号。如果你喜欢干净整洁的东西,你可以定期运行一个重新编号的程序,甚至可以输出数字。

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

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