热烈祝贺台州朗动科技的站长论坛隆重上线!(2012-05-28)    热烈庆祝伟大的祖国60周年生日 点击进来我们一起为她祝福吧(2009-09-26)    站长论坛禁止发布广告,一经发现立即删除。谢谢各位合作!.(2009-08-08)    热烈祝贺台州网址导航全面升级,全新版本上线!希望各位一如既往地支持台州网址导航的发展.(2009-03-28)    台州站长论坛恭祝各位新年快乐,牛年行大运!(2009-01-24)    台州Link正式更名为台州网址导航,专业做以台州网址为主的网址导航!(2008-05-23)    热烈祝贺台州Link资讯改名为中国站长资讯!希望在以后日子里得到大家的大力支持和帮助!(2008-04-10)    热烈祝贺台州Link论坛改名为台州站长论坛!希望大家继续支持和鼓励!(2008-04-10)    台州站长论坛原[社会琐碎]版块更名为[生活百科]版块!(2007-09-05)    特此通知:新台州站长论坛的数据信息全部升级成功!">特此通知:新台州站长论坛的数据信息全部升级成功!(2007-09-01)    台州站长论坛对未通过验证的会员进行合理的清除,请您谅解(2007-08-30)    台州网址导航|上网导航诚邀世界各地的网站友情链接和友谊联盟,共同引领网站导航、前进!(2007-08-30)    禁止发广告之类的帖,已发现立即删除!(2007-08-30)    希望各位上传与下载有用资源和最新信息(2007-08-30)    热烈祝贺台州站长论坛全面升级成功,全新上线!(2007-08-30)    
便民网址导航,轻松网上冲浪。
台州维博网络专业开发网站门户平台系统
您当前的位置: 首页 » MySQL/MSSQL编程 » SQL实现最优坐地铁方案

SQL实现最优坐地铁方案

论坛链接
  • SQL实现最优坐地铁方案
  • 发布时间:2007-11-30 12:42:30    浏览数:6728    发布者:superadmin    设置字体【   
坐地铁有时候不一定要坐最少站的,有时是希望能坐换乘次数最少的,应该怎么改造才能把所有的方案都取出来,然后按换乘次数、经过站点数依次排序?
  lineID state orderid

  1 广州东 1

  1 体育中心2

  1 体育西 3

  1 烈士陵园4

  1 公园前 6

  1 西门口 7

  2 火车站 1

  2 纪念堂 2

  2 公园前 3

  2 中大 4

  2 客村 5

  2 琶洲 6

  2 万胜围 7

  3 广州东 1

  3 体育西 2

  3 珠江新城3

  3 客村 4

  3 市桥 5

  4 万胜围 1

  4 金洲 2

  如上面数据,想查询“广州东”至“中大”,大家通过程序计算列出全部的方案。

 Peak Wong:

  SQL code  

DECLARE @tb TABLE(
lineID int, state nvarchar(10), orderid int)
INSERT @tb
SELECT 1, N'广州东', 1 UNION ALL
SELECT 1, N'体育中心', 2 UNION ALL
SELECT 1, N'体育西', 3 UNION ALL
SELECT 1, N'烈士陵园', 4 UNION ALL
SELECT 1, N'公园前', 6 UNION ALL
SELECT 1, N'西门口', 7 UNION ALL
SELECT 2, N'火车站', 1 UNION ALL
SELECT 2, N'纪念堂', 2 UNION ALL
SELECT 2, N'公园前', 3 UNION ALL
SELECT 2, N'中大', 4 UNION ALL
SELECT 2, N'客村', 5 UNION ALL
SELECT 2, N'琶洲', 6 UNION ALL
SELECT 2, N'万胜围', 7 UNION ALL
SELECT 3, N'广州东', 1 UNION ALL
SELECT 3, N'体育西', 2 UNION ALL
SELECT 3, N'珠江新城', 3 UNION ALL
SELECT 3, N'客村', 4 UNION ALL
SELECT 3, N'市桥', 5 UNION ALL
SELECT 4, N'万胜围', 1 UNION ALL
SELECT 4, N'金洲', 2
DECLARE
@state_start nvarchar(10),
@state_stop nvarchar(10)
SELECT
@state_start = N'广州东',
@state_stop = N'中大'

-- 查询
DECLARE @re TABLE(
path nvarchar(max),
state_count int,
start_lineID int,
start_state nvarchar(10),
current_lineID int,
current_state nvarchar(10),
current_orderid int,
flag int,
lineIDs nvarchar(max),
level int
)
DECLARE
@level int,
@rows int
SET
@level = 0

-- 开始
INSERT @re
SELECT
path = CONVERT(nvarchar(max),
RTRIM(A.lineID) + N'{'
+ RTRIM(A.orderid) + N'.' + A.state
),
state_count = 0,
start_lineID = A.lineID,
start_state = A.state,
current_lineID = A.lineID,
current_state = A.state,
current_orderid = A.orderid,
flag = CASE
WHEN A.state = @state_stop THEN 0
ELSE NULL END,
lineIDs = ',' + RTRIM(A.lineID) + ',',
level = -(@level + 1)
FROM @tb A
WHERE state = @state_start
SET @rows = @@ROWCOUNT
WHILE @rows > 0
BEGIN
SELECT
@level = @level + 1
INSERT @re
-- 同一 LineID
SELECT
path = CONVERT(nvarchar(max),
A.path
+ N'->'
+ RTRIM(B.orderid) + N'.' + B.state
),
state_count = A.state_count + 1,
A.start_lineID, A.start_state,
current_lineID = B.lineID,
current_state = B.state,
current_orderid = B.orderid,
flag = CASE
WHEN B.state = @state_stop THEN 0
ELSE A.flag END,
A.lineIDs,
level = @level
FROM @re A, @tb B
WHERE A.flag <> 0
AND A.level = @level - 1
AND A.current_lineID = B.lineID
AND A.current_orderid + A.flag = B.orderid

UNION ALL
-- 不同 LineID
SELECT
path = CONVERT(nvarchar(max),
A.path + N')->'
+ RTRIM(B.lineID) + N'{'
+ RTRIM(B.orderid) + N'.' + B.state
),
state_count = A.state_count + 1,
A.start_lineID, A.start_state,
current_lineID = B.lineID,
current_state = B.state,
current_orderid = B.orderid,
flag = CASE
WHEN B.state = @state_stop THEN 0
ELSE NULL END,
A.lineIDs + RTRIM(B.lineID) + ',',
level = - @level
FROM @re A, @tb B
WHERE A.flag <> 0
AND state_count = @level - 1
AND A.current_lineID <> B.lineID
AND A.current_state = B.state
AND NOT EXISTS(
SELECT * FROM @re
WHERE CHARINDEX(',' + RTRIM(B.lineID) + ',', A.lineIDs) > 0)
SET @rows = @@ROWCOUNT

INSERT @re
-- 不同 LineID 的第1站正向
SELECT
path = CONVERT(nvarchar(max),
A.path
+ N'->'
+ RTRIM(B.orderid) + N'.' + B.state
),
state_count = A.state_count + 1,
A.start_lineID, A.start_state,
current_lineID = B.lineID,
current_state = B.state,
current_orderid = B.orderid,
flag = CASE
WHEN B.state = @state_stop THEN 0
ELSE 1 END,
A.lineIDs,
level = @level
FROM @re A, @tb B
WHERE A.flag IS NULL
AND A.level = - @level
AND A.current_lineID = B.lineID
AND A.current_orderid + 1 = B.orderid
UNION ALL
-- 不同 LineID 的第1站反向
SELECT
path = CONVERT(nvarchar(max),
A.path
+ N'->'
+ RTRIM(B.orderid) + N'.' + B.state
),
state_count = A.state_count + 1,
A.start_lineID, A.start_state,
current_lineID = B.lineID,
current_state = B.state,
current_orderid = B.orderid,
flag = CASE
WHEN B.state = @state_stop THEN 0
ELSE - 1 END,
A.lineIDs,
level = @level
FROM @re A, @tb B
WHERE A.flag IS NULL
AND A.level = - @level
AND A.current_lineID = B.lineID
AND A.current_orderid - 1 = B.orderid

SET @rows = @rows + @@ROWCOUNT
END

SELECT
-- *,
path = path + N'}',
state_count
FROM @re
WHERE flag = 0



  结果(数字5,7是要经过多少站:

  3{1.广州东-> 2.体育西-> 3.珠江新城-> 4.客村)-> 2{5.客村-> 4.中大} 5

  1{1.广州东-> 2.体育中心-> 3.体育西)-> 3{2.体育西-> 3.珠江新城-> 4.客村)-> 2{5.客村-> 4.中大} 7
      下面这个包含要换乘多少次

  SQL code

DECLARE @tb TABLE(
lineID int, state nvarchar(10), orderid int)
INSERT @tb
SELECT 1, N'广州东', 1 UNION ALL
SELECT 1, N'体育中心', 2 UNION ALL
SELECT 1, N'体育西', 3 UNION ALL
SELECT 1, N'烈士陵园', 4 UNION ALL
SELECT 1, N'公园前', 6 UNION ALL
SELECT 1, N'西门口', 7 UNION ALL
SELECT 2, N'火车站', 1 UNION ALL
SELECT 2, N'纪念堂', 2 UNION ALL
SELECT 2, N'公园前', 3 UNION ALL
SELECT 2, N'中大', 4 UNION ALL
SELECT 2, N'客村', 5 UNION ALL
SELECT 2, N'琶洲', 6 UNION ALL
SELECT 2, N'万胜围', 7 UNION ALL
SELECT 3, N'广州东', 1 UNION ALL
SELECT 3, N'体育西', 2 UNION ALL
SELECT 3, N'珠江新城', 3 UNION ALL
SELECT 3, N'客村', 4 UNION ALL
SELECT 3, N'市桥', 5 UNION ALL
SELECT 4, N'万胜围', 1 UNION ALL
SELECT 4, N'金洲', 2
DECLARE
@state_start nvarchar(10),
@state_stop nvarchar(10)
SELECT
@state_start = N'广州东',
@state_stop = N'中大'

-- 查询
DECLARE @re TABLE(
path nvarchar(max),
state_count int,
line_count int,
start_lineID int,
start_state nvarchar(10),
current_lineID int,
current_state nvarchar(10),
current_orderid int,
flag int,
lineIDs nvarchar(max),
level int
)
DECLARE
@level int,
@rows int
SET
@level = 0

-- 开始
INSERT @re
SELECT
path = CONVERT(nvarchar(max),
RTRIM(A.lineID) + N'{'
+ RTRIM(A.orderid) + N'.' + A.state
),
state_count = 0,
line_count = 1,
start_lineID = A.lineID,
start_state = A.state,
current_lineID = A.lineID,
current_state = A.state,
current_orderid = A.orderid,
flag = CASE
WHEN A.state = @state_stop THEN 0
ELSE NULL END,
lineIDs = ',' + RTRIM(A.lineID) + ',',
level = -(@level + 1)
FROM @tb A
WHERE state = @state_start
SET @rows = @@ROWCOUNT
WHILE @rows > 0
BEGIN
SELECT
@level = @level + 1
INSERT @re
-- 同一 LineID
SELECT
path = CONVERT(nvarchar(max),
A.path
+ N'->'
+ RTRIM(B.orderid) + N'.' + B.state
),
state_count = A.state_count + 1,
A.line_count,
A.start_lineID, A.start_state,
current_lineID = B.lineID,
current_state = B.state,
current_orderid = B.orderid,
flag = CASE
WHEN B.state = @state_stop THEN 0
ELSE A.flag END,
A.lineIDs,
level = @level
FROM @re A, @tb B
WHERE A.flag <> 0
AND A.level = @level - 1
AND A.current_lineID = B.lineID
AND A.current_orderid + A.flag = B.orderid

UNION ALL
-- 不同 LineID
SELECT
path = CONVERT(nvarchar(max),
A.path + N')->'
+ RTRIM(B.lineID) + N'{'
+ RTRIM(B.orderid) + N'.' + B.state
),
state_count = A.state_count + 1,
line_count = A.line_count + 1,
A.start_lineID, A.start_state,
current_lineID = B.lineID,
current_state = B.state,
current_orderid = B.orderid,
flag = CASE
WHEN B.state = @state_stop THEN 0
ELSE NULL END,
A.lineIDs + RTRIM(B.lineID) + ',',
level = - @level
FROM @re A, @tb B
WHERE A.flag <> 0
AND state_count = @level - 1
AND A.current_lineID <> B.lineID
AND A.current_state = B.state
AND NOT EXISTS(
SELECT * FROM @re
WHERE CHARINDEX(',' + RTRIM(B.lineID) + ',', A.lineIDs) > 0)
SET @rows = @@ROWCOUNT

INSERT @re
-- 不同 LineID 的第1站正向
SELECT
path = CONVERT(nvarchar(max),
A.path
+ N'->'
+ RTRIM(B.orderid) + N'.' + B.state
),
state_count = A.state_count + 1,
A.line_count,
A.start_lineID, A.start_state,
current_lineID = B.lineID,
current_state = B.state,
current_orderid = B.orderid,
flag = CASE
WHEN B.state = @state_stop THEN 0
ELSE 1 END,
A.lineIDs,
level = @level
FROM @re A, @tb B
WHERE A.flag IS NULL
AND A.level = - @level
AND A.current_lineID = B.lineID
AND A.current_orderid + 1 = B.orderid
UNION ALL
-- 不同 LineID 的第1站反向
SELECT
path = CONVERT(nvarchar(max),
A.path
+ N'->'
+ RTRIM(B.orderid) + N'.' + B.state
),
state_count = A.state_count + 1,
A.line_count,
A.start_lineID, A.start_state,
current_lineID = B.lineID,
current_state = B.state,
current_orderid = B.orderid,
flag = CASE
WHEN B.state = @state_stop THEN 0
ELSE - 1 END,
A.lineIDs,
level = @level
FROM @re A, @tb B
WHERE A.flag IS NULL
AND A.level = - @level
AND A.current_lineID = B.lineID
AND A.current_orderid - 1 = B.orderid

SET @rows = @rows + @@ROWCOUNT
END

SELECT
-- *,
path = path + N'}',
line_count,
state_count
FROM @re
WHERE flag = 0




  结果, 2/3 是换乘次数(应该减一, 将上面代码中初始化 line_count 的地方从1改成0即可):

  3{1.广州东-> 2.体育西-> 3.珠江新城-> 4.客村)-> 2{5.客村-> 4.中大} 2 5

  1{1.广州东-> 2.体育中心-> 3.体育西)-> 3{2.体育西-> 3.珠江新城-> 4.客村)-> 2{5.客村-> 4.中大} 3 7

  数据的问题, 我的算法依赖于 orderid 来搜索下一站, 如果这个不连续, 则无法搜索下一站

  所以把数据改成下面的就行了

  SQL code 

DECLARE @tb TABLE(
lineID int, state nvarchar(10), orderid int)
INSERT @tb
SELECT 1, N'广州东', 1 UNION ALL
SELECT 1, N'体育中心', 2 UNION ALL
SELECT 1, N'体育西', 3 UNION ALL
SELECT 1, N'烈士陵园', 4 UNION ALL
--SELECT 1, N'公园前', 6 UNION ALL -- 这里站点断开了, 我的搜索要求连续
--SELECT 1, N'西门口', 7 UNION ALL
SELECT 1, N'公园前', 5 UNION ALL -- 这里站点断开了, 我的搜索要求连续
SELECT 1, N'西门口', 6 UNION ALL
SELECT 2, N'火车站', 1 UNION ALL
SELECT 2, N'纪念堂', 2 UNION ALL
SELECT 2, N'公园前', 3 UNION ALL
SELECT 2, N'中大', 4 UNION ALL
SELECT 2, N'客村', 5 UNION ALL
SELECT 2, N'琶洲', 6 UNION ALL
SELECT 2, N'万胜围', 7 UNION ALL
SELECT 3, N'广州东', 1 UNION ALL
SELECT 3, N'体育西', 2 UNION ALL
SELECT 3, N'珠江新城', 3 UNION ALL
SELECT 3, N'客村', 4 UNION ALL
SELECT 3, N'市桥', 5 UNION ALL
SELECT 4, N'万胜围', 1 UNION ALL
SELECT 4, N'金洲', 2


  修改后的执行结果(换乘数已经改成初始化为0)

  3{1.广州东-> 2.体育西-> 3.珠江新城-> 4.客村)-> 2{5.客村-> 4.中大} 1 5

  3{1.广州东-> 2.体育西)-> 1{3.体育西-> 4.烈士陵园-> 5.公园前)-> 2{3.公园前-> 4.中大} 2 6

  1{1.广州东-> 2.体育中心-> 3.体育西-> 4.烈士陵园-> 5.公园前)-> 2{3.公园前-> 4.中大} 1 6

  1{1.广州东-> 2.体育中心-> 3.体育西)-> 3{2.体育西-> 3.珠江新城-> 4.客村)-> 2{5.客村-> 4.中大} 2 7

  如果 orderid 在实际数据中确实有不连续的问题, 则可以在处理之前先把数据导到临时表, 生成连续的 orderid, 再用我的算法来查询结果。这次的算法相比之前的算法有改进, 只有换乘才会判断是否已经走过此线路, 其他方面也略有调整, 应该比以前的好。你可以测试一下!

  实际使用时, 算法上可以稍做调到:

  1. 直接计算出 next_orderid, 而不是每次用 flag 去算, 这样可以提高 join 效率

  2. 表变量改成临时表, 这样可以在相关的列上建立索引, 从而更快的与原表 join (当然, 原表相关的列上也要有索引)
娱乐休闲专区A 影视预告B 音乐咖啡C 英语阶梯D 生活百科
网页编程专区E AMPZF HTMLG CSSH JSI ASPJ PHPK JSPL MySQLM AJAX
Linux技术区 N 系统管理O 服务器架设P 网络/硬件Q 编程序开发R 内核/嵌入
管理中心专区S 发布网址T 版主议事U 事务处理