根据一列对 Table 进行排序,然后根据其他 2 列查找下一行
Sort a Table based on one column, then find the next row based on 2 other Columns
好吧,伙计们..我对这个有点难过..
我有 table 个项目在我正在整合的地点。这些项目分布在不同的行之间(如仓库)。我已经计算出什么要发送到哪里,以及起始位置和结束位置之间的距离。
现在,我需要开发一个从最短移动开始的报告,然后从它的 FINISH 位置找到下一个最短移动,其 START 位置最接近第一个移动的 FINISH 位置...所以如果我移动 obj A从仓库第 20 行到第 30 行,我希望我的下一行是下一个最近的移动,可能在第 30 行,这也是最短的距离。
item | start_loc | end_loc | distance
A | 5 | 10 | 5
B | 14 | 11 | 3
C | 20 | 1 | 19
D | 10 | 13 | 3
E | 10 | 5 | 5
F | 10 | 6 | 4
所以上面的table会被排序
D, B, F, A, E, C
基本上我想优化行程量,空手而归的时间最少..
使用 ColdFusion 和 SQL 来做到这一点..
根据以下评论编辑:
我会尝试进一步澄清。上面的 table 将被排序为 D、B、F、A、E、C,因为:
D 的距离最短 - 3;
B 是下一个接近 D 的末端 (13 --> 14);
F 因为移动 B 在 11 处结束,10 是下一个最近的移动行,并且 F 在该行中移动距离最短;
A bc F 结束于 6,A 开始于 5;
E bc A 在 10 结束,E 从 10 开始;
C因为最不方便(最长,没有什么结束)所以最后
更新:
我调整了下面选定的答案以与我的 tables 等一起使用。但是,它跳过了其中一行,我不确定为什么?
<!-- Add some columns to the working table for calculations -->
<cfquery name="updateWorking" datasource="planning" dbtype="obdc">
ALTER TABLE working
ADD move_distance FLOAT;
ALTER TABLE working
ADD start_loc FLOAT;
ALTER TABLE working
ADD finish_loc FLOAT;
ALTER TABLE working
ADD move_order INT;
</cfquery>
<cfquery name="updateWorking2" datasource="planning" dbtype="obdc">
UPDATE working
SET start_loc = LEFT(Storage_Bin, 5)
WHERE marked_consolidate_loc IS NOT NULL;
UPDATE working
SET finish_loc = LEFT(marked_consolidate_loc, 5)
WHERE marked_consolidate_loc IS NOT NULL;
UPDATE working
SET move_distance = finish_loc - start_loc
WHERE marked_consolidate_loc IS NOT NULL;
UPDATE working
SET move_distance = ABS(move_distance)
where move_distance < 0
</cfquery>
<!-- Query to show all the moves in order by distance, shortest first -->
<cfquery name="report" datasource="planning" dbtype="obdc">
SELECT id, Material, marked_consolidate, marked_consolidate_loc, marked_consolidate_su,
max_pallet, mixed_skid, Storage_Bin, Storage_Unit, move_distance, finish_loc, start_loc
FROM working
WHERE marked_consolidate IS NOT NULL
AND mixed_skid = 0
ORDER BY move_distance ASC
</cfquery>
<!-- What is the shortest move? Do it first -->
<cfquery name="firstMove" datasource="planning" dbtype="obdc" maxRows="1">
Select id, Material, marked_consolidate, marked_consolidate_loc, marked_consolidate_su,
max_pallet, mixed_skid, Storage_Bin, Storage_Unit, move_distance, finish_loc, start_loc
FROM working
WHERE marked_consolidate IS NOT NULL
AND mixed_skid = 0
ORDER BY move_distance ASC, start_loc ASC
</cfquery>
<!--- set the Move Number --->
<cfset moveNumber = 1>
<!-- List to remember ID of moves that have been completed -->
<cfset tripSequence = ''>
<!-- Update the first selection as the first move -->
<cfquery name= "updateMove" datasource="planning" dbtype="obdc">
UPDATE working
SET move_order = #moveNumber#
WHERE id = #firstMove.id#;
</cfquery>
<cfset moveNumber = moveNumber + 1>
<cfset tripSequence = ListAppend(tripSequence, "#firstMove.id#")>
<cfset lastMoveFinish = #firstMove.finish_loc#>
<!--- number of trips remaining --->
<cfset numberOfTrips = (report.recordCount) - 1>
<!-- Loop through the whole table -->
<cfloop from="1" to="#numberOfTrips#" index="i">
<!--- determine next move to compare to --->
<cfloop query="report">
<!--- Has it been moved already?--->
<cfif listContains(tripSequence, #report.id#)>
<!-- If so, continue to next row -->
<cfcontinue>
</cfif>
<!-- If not, remember this one -->
<cfset nextLocationID = report.id>
<cfset nextLocationFinishLoc = report.finish_loc>
<cfset nextLocationDist = abs(lastMoveFinish - report.start_loc)>
</cfloop>
<!--- compare this move with other moves, if the next one is shorter remember it --->
<cfloop query="report">
<!--- Has it been moved already? --->
<cfif listContains(tripSequence, #report.id#)>
<cfcontinue>
</cfif>
<!-- How far is this move from your current location? -->
<cfset nextLocationDistance = abs(lastMoveFinish - report.start_loc)>
<!-- If this move is closer to you than the one you selected above, remember it instead -->
<cfif nextLocationDistance LT nextLocationDist>
<cfset nextLocationID = report.id>
<cfset nextLocationFinishLoc = report.finish_loc>
<cfset nextLocationDist = abs(lastMoveFinish - report.start_loc)>
</cfif>
</cfloop>
<!-- once you have the closest move, remember it and update the column -->
<cfset tripSequence = ListAppend(tripSequence, nextLocationID)>
<!-- Update the move column -->
<cfquery name= "updateMove" datasource="planning" dbtype="obdc">
UPDATE working
SET move_order = #moveNumber#
WHERE id = #nextLocationID#;
</cfquery>
<!-- Increment the Move Number -->
<cfset moveNumber = moveNumber + 1>
<!--- set the ending of your last move --->
<cfset lastMoveFinish = nextLocationFinishLoc>
</cfloop>
<!-- BELOW IS OUTPUT OF THE REPORT -->
<body>
<!-- Build the report -->
<table border='1'>
<tr>
<th colspan="7">
<h2>Consolidation Report</h2>
</th>
</tr>
<tr>
<td>Move Order</td>
<td>Current Loc</td>
<td>Current SU</td>
<td>Item Number</td>
<td>Qty To Move</td>
<td>Moved To Loc</td>
<td>Moved To SU</td>
</tr>
<!-- Query to show all the moves in order by distance, shortest first -->
<cfquery name="showReport" datasource="planning" dbtype="obdc">
SELECT Material, marked_consolidate, marked_consolidate_loc, marked_consolidate_su,
Storage_Bin, Storage_Unit, move_order
FROM working
WHERE marked_consolidate IS NOT NULL
AND mixed_skid = 0
ORDER BY move_order
</cfquery>
<cfloop query="showReport">
<tr>
<cfoutput>
<td>#showReport.move_order#</td>
<td>#showReport.Storage_Bin#</td>
<td>#showReport.Storage_Unit#</td>
<td>#showReport.Material#</td>
<td>#showReport.marked_consolidate#</td>
<td>#showReport.marked_consolidate_loc#</td>
<td>#showReport.marked_consolidate_su#</td>
</cfoutput>
</tr>
</cfloop>
</table>
<cfoutput>#tripSequence#</cfoutput>
<body>
输出是一个有 49 行的 table.. 但是其中一行 Move Number 是空的,它跳过了 Move Number: 48。有什么想法吗?
所有行在逻辑上都是正确的,它只是跳过了 48 并且没有将 Null 行放在它应该在的位置(逻辑上应该在移动 30 左右)。
解决 TSP,嗯?这是我为您提供的解决方案,除非您 运行 数千个节点,否则它的性能应该很好。
<cfset data = queryNew(
"item,start_loc,end_loc,distance",
"VARCHAR,INTEGER,INTEGER,INTEGER",
[
[ "A", 5, 10, 5 ],
[ "B", 14, 11, 3 ],
[ "C", 20, 1, 19 ],
[ "D", 10, 13, 3 ],
[ "E", 10, 5, 5 ],
[ "F", 10, 6, 4 ]
]
)>
<cfset tripSequence = []>
<!--- BEGIN: determine first item --->
<cfquery name="closestLocation" dbType="query" maxRows="1">
SELECT
*
FROM
[data]
ORDER BY
[distance] ASC,
[start_loc] ASC
</cfquery>
<!--- add item --->
<cfset tripSequence.add(closestLocation.item)>
<!--- END: determine first item --->
<!--- number of trips remaining --->
<cfset numberOfTrips = (data.recordCount - 1)>
<cfloop from="1" to="#numberOfTrips#" index="i">
<!--- BEGIN: determine next trip to compare to --->
<cfloop query="data">
<!--- must not have been done already --->
<cfif arrayFind(tripSequence, data.item)>
<cfcontinue>
</cfif>
<cfset nextLocation = {
item: data.item,
end_loc: data.end_loc,
distance: abs(closestLocation.end_loc - data.start_loc)
}>
</cfloop>
<!--- END: determine next trip to compare to --->
<!--- BEGIN: compare with remaining trips --->
<cfloop query="data">
<!--- must not have been done already --->
<cfif arrayFind(tripSequence, data.item)>
<cfcontinue>
</cfif>
<cfset nextLocationDistance = abs(closestLocation.end_loc - data.start_loc)>
<cfif nextLocationDistance lt nextLocation.distance>
<cfset nextLocation = {
item: data.item,
end_loc: data.end_loc,
distance: nextLocationDistance
}>
</cfif>
</cfloop>
<!--- END: compare with remaining trips --->
<!--- add item --->
<cfset tripSequence.add(nextLocation.item)>
<!--- take item as base for the next iteration --->
<cfset closestLocation = nextLocation>
</cfloop>
<cfoutput>#arrayToList(tripSequence, ", ")#</cfoutput>
有趣的问题,sql 和逻辑难题。显然是递归的工作,为了简单起见,使用了一个 while 循环:
create table #examplework (item varchar(1), start_Loc int, end_loc int, distance int)
insert into #examplework
select 'a', 5, 10, 5
union
select 'b', 14, 11, 3
union
select 'c', 20, 1, 19
union
select 'd', 10, 13, 3
union
select 'e', 10, 5, 5
union
select 'f', 10, 6, 4
create table #worktable (Workingpath varchar(900), Current_Step varchar(1))
declare @step varchar(1) = (select top 1 item from #examplework order by distance)
declare @path varchar(900) = ''
while @step is not null
begin
insert into #worktable
select concat(@path, ',' , @step) as Workingpath, @step as CurrentStep
set @step = (select top 1 ew1.item
from #examplework ew
join #examplework ew1 on ew.item != ew1.item
where ew.item = @step
and ew1.item not in (select Current_Step from #worktable)
order by abs(ew.end_loc - ew1.start_Loc))
set @path = (select top 1 Workingpath from #worktable order by len(Workingpath) desc)
end
select top 1 * from #worktable
order by LEN(workingpath) desc
因此,推销员悖论再次在 sql 中得到解决(该死的大学作业)。为了单步执行它,我设置了 step 变量,然后抓取最近没有出现在作品中的项目 table。一旦所有项目都在工作 table 中,while 循环就会关闭。希望对您有所帮助。
edit 糟糕,如果这是 TSP,您不能简单地选择一个节点并希望最近的节点通向最佳路线。你需要用暴力破解它,查看所有可能的组合并选择需要最少行程的组合。这是一个例子:
select CONCAT(a.item, ',', b.item, ',', c.item, ',', d.item, ',', e.item, ',', f.item) as route,
sum(abs(a.end_loc - b.start_Loc) + abs(b.end_loc - c.start_Loc) + abs(c.end_loc - d.start_Loc) + ABS(d.end_loc - e.start_Loc) + abs(e.end_loc - f.start_Loc)) as distance
from
#examplework a
join #examplework b on b.item != a.item
join #examplework c on c.item != a.item and c.item != b.item
join #examplework d on d.item != a.item and d.item != b.item and d.item != c.item
join #examplework e on e.item != a.item and e.item != b.item and e.item != c.item and e.item != d.item
join #examplework f on f.item != a.item and f.item != b.item and f.item != c.item and f.item != d.item and f.item != e.item
group by CONCAT(a.item, ',', b.item, ',', c.item, ',', d.item, ',', e.item, ',', f.item)
order by distance
从字面上看,这是解决 TSP 的唯一方法,其他方法将为您找到一条好路线,蛮力法将为您找到最佳路线。
好吧,伙计们..我对这个有点难过..
我有 table 个项目在我正在整合的地点。这些项目分布在不同的行之间(如仓库)。我已经计算出什么要发送到哪里,以及起始位置和结束位置之间的距离。
现在,我需要开发一个从最短移动开始的报告,然后从它的 FINISH 位置找到下一个最短移动,其 START 位置最接近第一个移动的 FINISH 位置...所以如果我移动 obj A从仓库第 20 行到第 30 行,我希望我的下一行是下一个最近的移动,可能在第 30 行,这也是最短的距离。
item | start_loc | end_loc | distance
A | 5 | 10 | 5
B | 14 | 11 | 3
C | 20 | 1 | 19
D | 10 | 13 | 3
E | 10 | 5 | 5
F | 10 | 6 | 4
所以上面的table会被排序
D, B, F, A, E, C
基本上我想优化行程量,空手而归的时间最少..
使用 ColdFusion 和 SQL 来做到这一点..
根据以下评论编辑: 我会尝试进一步澄清。上面的 table 将被排序为 D、B、F、A、E、C,因为: D 的距离最短 - 3; B 是下一个接近 D 的末端 (13 --> 14); F 因为移动 B 在 11 处结束,10 是下一个最近的移动行,并且 F 在该行中移动距离最短; A bc F 结束于 6,A 开始于 5; E bc A 在 10 结束,E 从 10 开始; C因为最不方便(最长,没有什么结束)所以最后
更新: 我调整了下面选定的答案以与我的 tables 等一起使用。但是,它跳过了其中一行,我不确定为什么?
<!-- Add some columns to the working table for calculations -->
<cfquery name="updateWorking" datasource="planning" dbtype="obdc">
ALTER TABLE working
ADD move_distance FLOAT;
ALTER TABLE working
ADD start_loc FLOAT;
ALTER TABLE working
ADD finish_loc FLOAT;
ALTER TABLE working
ADD move_order INT;
</cfquery>
<cfquery name="updateWorking2" datasource="planning" dbtype="obdc">
UPDATE working
SET start_loc = LEFT(Storage_Bin, 5)
WHERE marked_consolidate_loc IS NOT NULL;
UPDATE working
SET finish_loc = LEFT(marked_consolidate_loc, 5)
WHERE marked_consolidate_loc IS NOT NULL;
UPDATE working
SET move_distance = finish_loc - start_loc
WHERE marked_consolidate_loc IS NOT NULL;
UPDATE working
SET move_distance = ABS(move_distance)
where move_distance < 0
</cfquery>
<!-- Query to show all the moves in order by distance, shortest first -->
<cfquery name="report" datasource="planning" dbtype="obdc">
SELECT id, Material, marked_consolidate, marked_consolidate_loc, marked_consolidate_su,
max_pallet, mixed_skid, Storage_Bin, Storage_Unit, move_distance, finish_loc, start_loc
FROM working
WHERE marked_consolidate IS NOT NULL
AND mixed_skid = 0
ORDER BY move_distance ASC
</cfquery>
<!-- What is the shortest move? Do it first -->
<cfquery name="firstMove" datasource="planning" dbtype="obdc" maxRows="1">
Select id, Material, marked_consolidate, marked_consolidate_loc, marked_consolidate_su,
max_pallet, mixed_skid, Storage_Bin, Storage_Unit, move_distance, finish_loc, start_loc
FROM working
WHERE marked_consolidate IS NOT NULL
AND mixed_skid = 0
ORDER BY move_distance ASC, start_loc ASC
</cfquery>
<!--- set the Move Number --->
<cfset moveNumber = 1>
<!-- List to remember ID of moves that have been completed -->
<cfset tripSequence = ''>
<!-- Update the first selection as the first move -->
<cfquery name= "updateMove" datasource="planning" dbtype="obdc">
UPDATE working
SET move_order = #moveNumber#
WHERE id = #firstMove.id#;
</cfquery>
<cfset moveNumber = moveNumber + 1>
<cfset tripSequence = ListAppend(tripSequence, "#firstMove.id#")>
<cfset lastMoveFinish = #firstMove.finish_loc#>
<!--- number of trips remaining --->
<cfset numberOfTrips = (report.recordCount) - 1>
<!-- Loop through the whole table -->
<cfloop from="1" to="#numberOfTrips#" index="i">
<!--- determine next move to compare to --->
<cfloop query="report">
<!--- Has it been moved already?--->
<cfif listContains(tripSequence, #report.id#)>
<!-- If so, continue to next row -->
<cfcontinue>
</cfif>
<!-- If not, remember this one -->
<cfset nextLocationID = report.id>
<cfset nextLocationFinishLoc = report.finish_loc>
<cfset nextLocationDist = abs(lastMoveFinish - report.start_loc)>
</cfloop>
<!--- compare this move with other moves, if the next one is shorter remember it --->
<cfloop query="report">
<!--- Has it been moved already? --->
<cfif listContains(tripSequence, #report.id#)>
<cfcontinue>
</cfif>
<!-- How far is this move from your current location? -->
<cfset nextLocationDistance = abs(lastMoveFinish - report.start_loc)>
<!-- If this move is closer to you than the one you selected above, remember it instead -->
<cfif nextLocationDistance LT nextLocationDist>
<cfset nextLocationID = report.id>
<cfset nextLocationFinishLoc = report.finish_loc>
<cfset nextLocationDist = abs(lastMoveFinish - report.start_loc)>
</cfif>
</cfloop>
<!-- once you have the closest move, remember it and update the column -->
<cfset tripSequence = ListAppend(tripSequence, nextLocationID)>
<!-- Update the move column -->
<cfquery name= "updateMove" datasource="planning" dbtype="obdc">
UPDATE working
SET move_order = #moveNumber#
WHERE id = #nextLocationID#;
</cfquery>
<!-- Increment the Move Number -->
<cfset moveNumber = moveNumber + 1>
<!--- set the ending of your last move --->
<cfset lastMoveFinish = nextLocationFinishLoc>
</cfloop>
<!-- BELOW IS OUTPUT OF THE REPORT -->
<body>
<!-- Build the report -->
<table border='1'>
<tr>
<th colspan="7">
<h2>Consolidation Report</h2>
</th>
</tr>
<tr>
<td>Move Order</td>
<td>Current Loc</td>
<td>Current SU</td>
<td>Item Number</td>
<td>Qty To Move</td>
<td>Moved To Loc</td>
<td>Moved To SU</td>
</tr>
<!-- Query to show all the moves in order by distance, shortest first -->
<cfquery name="showReport" datasource="planning" dbtype="obdc">
SELECT Material, marked_consolidate, marked_consolidate_loc, marked_consolidate_su,
Storage_Bin, Storage_Unit, move_order
FROM working
WHERE marked_consolidate IS NOT NULL
AND mixed_skid = 0
ORDER BY move_order
</cfquery>
<cfloop query="showReport">
<tr>
<cfoutput>
<td>#showReport.move_order#</td>
<td>#showReport.Storage_Bin#</td>
<td>#showReport.Storage_Unit#</td>
<td>#showReport.Material#</td>
<td>#showReport.marked_consolidate#</td>
<td>#showReport.marked_consolidate_loc#</td>
<td>#showReport.marked_consolidate_su#</td>
</cfoutput>
</tr>
</cfloop>
</table>
<cfoutput>#tripSequence#</cfoutput>
<body>
输出是一个有 49 行的 table.. 但是其中一行 Move Number 是空的,它跳过了 Move Number: 48。有什么想法吗?
所有行在逻辑上都是正确的,它只是跳过了 48 并且没有将 Null 行放在它应该在的位置(逻辑上应该在移动 30 左右)。
解决 TSP,嗯?这是我为您提供的解决方案,除非您 运行 数千个节点,否则它的性能应该很好。
<cfset data = queryNew(
"item,start_loc,end_loc,distance",
"VARCHAR,INTEGER,INTEGER,INTEGER",
[
[ "A", 5, 10, 5 ],
[ "B", 14, 11, 3 ],
[ "C", 20, 1, 19 ],
[ "D", 10, 13, 3 ],
[ "E", 10, 5, 5 ],
[ "F", 10, 6, 4 ]
]
)>
<cfset tripSequence = []>
<!--- BEGIN: determine first item --->
<cfquery name="closestLocation" dbType="query" maxRows="1">
SELECT
*
FROM
[data]
ORDER BY
[distance] ASC,
[start_loc] ASC
</cfquery>
<!--- add item --->
<cfset tripSequence.add(closestLocation.item)>
<!--- END: determine first item --->
<!--- number of trips remaining --->
<cfset numberOfTrips = (data.recordCount - 1)>
<cfloop from="1" to="#numberOfTrips#" index="i">
<!--- BEGIN: determine next trip to compare to --->
<cfloop query="data">
<!--- must not have been done already --->
<cfif arrayFind(tripSequence, data.item)>
<cfcontinue>
</cfif>
<cfset nextLocation = {
item: data.item,
end_loc: data.end_loc,
distance: abs(closestLocation.end_loc - data.start_loc)
}>
</cfloop>
<!--- END: determine next trip to compare to --->
<!--- BEGIN: compare with remaining trips --->
<cfloop query="data">
<!--- must not have been done already --->
<cfif arrayFind(tripSequence, data.item)>
<cfcontinue>
</cfif>
<cfset nextLocationDistance = abs(closestLocation.end_loc - data.start_loc)>
<cfif nextLocationDistance lt nextLocation.distance>
<cfset nextLocation = {
item: data.item,
end_loc: data.end_loc,
distance: nextLocationDistance
}>
</cfif>
</cfloop>
<!--- END: compare with remaining trips --->
<!--- add item --->
<cfset tripSequence.add(nextLocation.item)>
<!--- take item as base for the next iteration --->
<cfset closestLocation = nextLocation>
</cfloop>
<cfoutput>#arrayToList(tripSequence, ", ")#</cfoutput>
有趣的问题,sql 和逻辑难题。显然是递归的工作,为了简单起见,使用了一个 while 循环:
create table #examplework (item varchar(1), start_Loc int, end_loc int, distance int)
insert into #examplework
select 'a', 5, 10, 5
union
select 'b', 14, 11, 3
union
select 'c', 20, 1, 19
union
select 'd', 10, 13, 3
union
select 'e', 10, 5, 5
union
select 'f', 10, 6, 4
create table #worktable (Workingpath varchar(900), Current_Step varchar(1))
declare @step varchar(1) = (select top 1 item from #examplework order by distance)
declare @path varchar(900) = ''
while @step is not null
begin
insert into #worktable
select concat(@path, ',' , @step) as Workingpath, @step as CurrentStep
set @step = (select top 1 ew1.item
from #examplework ew
join #examplework ew1 on ew.item != ew1.item
where ew.item = @step
and ew1.item not in (select Current_Step from #worktable)
order by abs(ew.end_loc - ew1.start_Loc))
set @path = (select top 1 Workingpath from #worktable order by len(Workingpath) desc)
end
select top 1 * from #worktable
order by LEN(workingpath) desc
因此,推销员悖论再次在 sql 中得到解决(该死的大学作业)。为了单步执行它,我设置了 step 变量,然后抓取最近没有出现在作品中的项目 table。一旦所有项目都在工作 table 中,while 循环就会关闭。希望对您有所帮助。
edit 糟糕,如果这是 TSP,您不能简单地选择一个节点并希望最近的节点通向最佳路线。你需要用暴力破解它,查看所有可能的组合并选择需要最少行程的组合。这是一个例子:
select CONCAT(a.item, ',', b.item, ',', c.item, ',', d.item, ',', e.item, ',', f.item) as route,
sum(abs(a.end_loc - b.start_Loc) + abs(b.end_loc - c.start_Loc) + abs(c.end_loc - d.start_Loc) + ABS(d.end_loc - e.start_Loc) + abs(e.end_loc - f.start_Loc)) as distance
from
#examplework a
join #examplework b on b.item != a.item
join #examplework c on c.item != a.item and c.item != b.item
join #examplework d on d.item != a.item and d.item != b.item and d.item != c.item
join #examplework e on e.item != a.item and e.item != b.item and e.item != c.item and e.item != d.item
join #examplework f on f.item != a.item and f.item != b.item and f.item != c.item and f.item != d.item and f.item != e.item
group by CONCAT(a.item, ',', b.item, ',', c.item, ',', d.item, ',', e.item, ',', f.item)
order by distance
从字面上看,这是解决 TSP 的唯一方法,其他方法将为您找到一条好路线,蛮力法将为您找到最佳路线。