我的数据库中有2个表:BusLines
& BusStops
。 BusLines
的每个实例都可以按特定顺序与其关联。为了使关联更易于管理(在现有行中删除或添加新的停靠点),关联表的设计具有以下结构:
id_BusLine | id_BusStop | id_NextBusStop | isFirstStop
这样做似乎是一个好主意,而不是给每个停止一个数字,一旦新的停止必须添加到行的开头,必须为每个记录更改(但如果你有一个更好的主意,我当然希望听到它。)
所以,重申一下:如何在每一行上创建一个SELECT语句,以便以正确的顺序拥有所有停靠点?因为我只用一个简单的ORDER BY来解决它......
答案 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
答案 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;