根据一列对 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 的唯一方法,其他方法将为您找到一条好路线,蛮力法将为您找到最佳路线。