基于一列对表进行排序,然后根据另外两列

时间:2015-09-23 18:37:35

标签: sql coldfusion coldfusion-9

好的伙计们......我对这一个感到有点难过..

我在合并的位置有一个项目表。这些项目分布在不同的行(如仓库)之间。我已经计算了发送到哪里以及起始位置和结束位置之间的距离。

现在,我需要开发一个以最短移动开始的报告,然后从其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

因此上面的表格将被订购

  

D,B,F,A,E,C

基本上我想优化旅行量,花最少的时间空手而归..

使用ColdFusion和SQL执行此操作..

根据以下评论进行修改: 我将尝试进一步澄清..上面的表格将被订购D,B,F,A,E,C因为: D的距离最短 - 3; B是下一个关闭D的结束(13-> 14); F因为移动B在11处结束,10是具有移动的下一个最近的行,并且F在该行中具有最短的移动距离; bc F以6结束,A从5开始; E bc A以10结束,E从10开始; C因为它是最不方便的(最长的,没有任何结束)所以它是最后的

更新 我调整了下面选择的答案来处理我的表格等等。但是,它正在跳过其中一行,我不确定为什么?

    <!-- 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行的表..但其中一行Move Number为空,它跳过Move Number:48。想法?

所有行在逻辑上都是正确的,它只是跳过48并且没有将Null行放在它应该的位置(逻辑上将在移动30周围)。

2 个答案:

答案 0 :(得分:1)

解决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>

答案 1 :(得分:0)

有趣的问题,sql和逻辑谜题。显然是一个递归的工作,用一个while循环来保持简单:

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

因此推销员悖论再次在sql中得到解决(darn college homework)。为了逐步执行它,我设置了步骤变量,然后抓取未出现在工作表中的最近项目。一旦所有项目都在工作表中,while循环就会关闭。希望有所帮助。

如果这是TSP,

编辑呐喊,你不能简单地选择一个节点并希望最近的导致最佳路线。你需要暴力破坏它,看看所有可能的组合并挑选一个需要最少旅行的组合。这是一个例子:

add_filter('login_redirect', function ($redirect_to, $request, $user){
    if (user_can($user, 'registered')){
        return bp_core_get_user_domain( $user->ID );
    }
}

这实际上是解决TSP的唯一方法,另一种方法将为您提供一条好路线,蛮力方法将为您提供最佳路线。