如何才能只选择一次获胜比赛并将其排除在后续考虑之外?

问题描述 投票:0回答:1

我有多次发货的商品。我对给定商品的订单数量超出了我的供应能力。我提出了一个优先级系统,可以对订单如何最适合给定的发货进行排名(考虑他们何时订购、何时请求接收订单以及他们作为客户的优先状态)。我创建了一个视图,将每个订单与传入的发货与该订单的给定发货的优先级分数/排名联系起来。我可以轻松确定第一批发货的获胜订单,但我无法将获胜订单排除在后续发货的获胜者之外。这是一个问题,因为如果给定订单在第一次发货中排名最高,那么它很可能在后续发货中排名最高。这样做的主要限制之一是我们希望实时查看哪些订单赢得了发货,因为最终用户将能够动态调整优先级分数/排名算法,并且他们希望看到由此产生的变化。如果这是不可能的,我可以轻松创建一个表来保存获胜者并循环发货并以这种方式处理它,但我希望有一种方法。

我想提供一个非常简单的例子来说明我正在尝试做的事情。我对不同的场景有更多的逻辑(例如,处理数量高于发货量的订单等),但这不是我正在努力解决的问题。

出货量(表)

发货 数量
A 10
B 10
C 20

订单(表)

订单ID 数量
1 5
2 5
3 10
4 20
5 20

ORDER_SHIPMENT_RANK(查看)

发货 订单ID SHIP_QTY 订单数量 排名
A 1 10 5 1000
A 2 10 5 900
A 3 10 10 500
A 4 10 20 400
A 5 10 20 100
B 1 10 5 1200
B 2 10 5 1100
B 3 10 10 800
B 4 10 20 500
B 5 10 20 200
C 1 20 5 1500
C 2 20 5 1300
C 3 20 10 1000
C 5 20 20 900
C 4 20 20 600

*发货 C 订单 5 的曲线球比订单 4 的排名更高

期望的结果:

发货 订单ID 订单数量
A 1 5
A 2 5
B 3 10
C 5 20

订单 4 永远不会获胜,因为它的优先级分数/排名在给定的发货中从来都不够高。

我尝试过使用分析函数,但不知道如何删除后续发货中订单的后续迭代。

我尝试使用分析函数来选择可以填充发货传入数量的最高订单,但我无法删除在先前发货中获胜的订单的后续迭代,不考虑将其用于后续发货发货。

这应该可以帮助您解决这个问题:

WITH SHIPMENTS AS (
     SELECT 'A' SHIPMENT, 10 QUANTITY FROM dual UNION ALL
     SELECT 'B' SHIPMENT, 10 QUANTITY FROM dual UNION ALL
     SELECT 'C' SHIPMENT, 20 QUANTITY FROM dual
     ),
     
     ORDERS AS (
     SELECT '1' ORDERID, 10 QUANTITY FROM dual UNION ALL
     SELECT '2' ORDERID, 10 QUANTITY FROM dual UNION ALL
     SELECT '3' ORDERID, 10 QUANTITY FROM dual UNION ALL
     SELECT '4' ORDERID, 10 QUANTITY FROM dual UNION ALL
     SELECT '5' ORDERID, 20 QUANTITY FROM dual
     ),
     
     ORDER_SHIPMENT_RANK AS ( --I'm only including this here for convenience
     SELECT 'A' SHIPMENT, '1' ORDERID, 10 SHIP_QTY, 5 ORDER_QTY, 1000 RANK FROM dual UNION ALL
     SELECT 'A' SHIPMENT, '2' ORDERID, 10 SHIP_QTY, 5 ORDER_QTY, 900 RANK FROM dual UNION ALL
     SELECT 'A' SHIPMENT, '3' ORDERID, 10 SHIP_QTY, 10 ORDER_QTY, 500 RANK FROM dual UNION ALL
     SELECT 'A' SHIPMENT, '4' ORDERID, 10 SHIP_QTY, 20 ORDER_QTY, 400 RANK FROM dual UNION ALL
     SELECT 'A' SHIPMENT, '5' ORDERID, 10 SHIP_QTY, 20 ORDER_QTY, 100 RANK FROM dual UNION ALL
     SELECT 'B' SHIPMENT, '1' ORDERID, 10 SHIP_QTY, 5 ORDER_QTY, 1200 RANK FROM dual UNION ALL
     SELECT 'B' SHIPMENT, '2' ORDERID, 10 SHIP_QTY, 5 ORDER_QTY, 1100 RANK FROM dual UNION ALL
     SELECT 'B' SHIPMENT, '3' ORDERID, 10 SHIP_QTY, 10 ORDER_QTY, 800 RANK FROM dual UNION ALL
     SELECT 'B' SHIPMENT, '4' ORDERID, 10 SHIP_QTY, 20 ORDER_QTY, 500 RANK FROM dual UNION ALL
     SELECT 'B' SHIPMENT, '5' ORDERID, 10 SHIP_QTY, 20 ORDER_QTY, 200 RANK FROM dual UNION ALL
     SELECT 'C' SHIPMENT, '1' ORDERID, 20 SHIP_QTY, 5 ORDER_QTY, 1500 RANK FROM dual UNION ALL
     SELECT 'C' SHIPMENT, '2' ORDERID, 20 SHIP_QTY, 5 ORDER_QTY, 1300 RANK FROM dual UNION ALL
     SELECT 'C' SHIPMENT, '3' ORDERID, 20 SHIP_QTY, 10 ORDER_QTY, 1000 RANK FROM dual UNION ALL
     SELECT 'C' SHIPMENT, '5' ORDERID, 20 SHIP_QTY, 20 ORDER_QTY, 900 RANK FROM dual UNION ALL
     SELECT 'C' SHIPMENT, '4' ORDERID, 20 SHIP_QTY, 20 ORDER_QTY, 600 RANK FROM dual 
     )
     
--This is just a base example/sample of something I was trying to leverage to determine the winning order
-- quantities that would be used BUT it returns the same order for multiple shipments (the issue at hand)
     SELECT    x.SHIPMENT
               ,x.ORDERID
               ,x.SHIP_QTY
               ,x.ORDER_QTY
               ,x.PREV_WINNER_QTY
               ,CASE WHEN x.SHIP_QTY - NVL(x.PREV_WINNER_QTY,0) > 0  THEN
                     LEAST(x.SHIP_QTY - NVL(x.PREV_WINNER_QTY,0) , x.ORDER_QTY) 
                ELSE 0
                END WINNING_QTY
     FROM      (
               SELECT    sr.*
                         ,COALESCE(
                         SUM(ORDER_QTY) OVER (PARTITION BY SHIPMENT ORDER BY SHIPMENT, RANK DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) 
                         ,0
                         ) PREV_WINNER_QTY
               FROM      ORDER_SHIPMENT_RANK sr
               ) x;
               
sql oracle window-functions oracle21c
1个回答
0
投票

这是一个非常不寻常的任务和系统。您以 SQL 代码形式提供的示例数据仍然与上面的表格不匹配,因此我将显示此答案中使用的数据:

WITH   --  S a m p l e   D a t a :
    SHIPMENTS AS 
        (   SELECT 'A' SHIPMENT, 10 QUANTITY FROM dual UNION ALL
            SELECT 'B' SHIPMENT, 10 QUANTITY FROM dual UNION ALL
            SELECT 'C' SHIPMENT, 20 QUANTITY FROM dual
        ),
    ORDERS AS 
        (   SELECT '1' ORDERID, 5 QUANTITY FROM dual UNION ALL
            SELECT '2' ORDERID, 5 QUANTITY FROM dual UNION ALL
            SELECT '3' ORDERID, 10 QUANTITY FROM dual UNION ALL
            SELECT '4' ORDERID, 20 QUANTITY FROM dual UNION ALL
            SELECT '5' ORDERID, 20 QUANTITY FROM dual
        ),
    ORDER_SHIPMENT_RANK AS --I'm only including this here for convenience
        ( SELECT 'A' SHIPMENT, '1' ORDERID, 10 SHIP_QTY, 5 ORDER_QTY, 1000 RANK FROM dual UNION ALL
            SELECT 'A' SHIPMENT, '2' ORDERID, 10 SHIP_QTY, 5 ORDER_QTY, 900 RANK FROM dual UNION ALL
            SELECT 'A' SHIPMENT, '3' ORDERID, 10 SHIP_QTY, 10 ORDER_QTY, 500 RANK FROM dual UNION ALL
            SELECT 'A' SHIPMENT, '4' ORDERID, 10 SHIP_QTY, 20 ORDER_QTY, 400 RANK FROM dual UNION ALL
            SELECT 'A' SHIPMENT, '5' ORDERID, 10 SHIP_QTY, 20 ORDER_QTY, 100 RANK FROM dual UNION ALL
            SELECT 'B' SHIPMENT, '1' ORDERID, 10 SHIP_QTY, 5 ORDER_QTY, 1200 RANK FROM dual UNION ALL
            SELECT 'B' SHIPMENT, '2' ORDERID, 10 SHIP_QTY, 5 ORDER_QTY, 1100 RANK FROM dual UNION ALL
            SELECT 'B' SHIPMENT, '3' ORDERID, 10 SHIP_QTY, 10 ORDER_QTY, 800 RANK FROM dual UNION ALL
            SELECT 'B' SHIPMENT, '4' ORDERID, 10 SHIP_QTY, 20 ORDER_QTY, 500 RANK FROM dual UNION ALL
            SELECT 'B' SHIPMENT, '5' ORDERID, 10 SHIP_QTY, 20 ORDER_QTY, 200 RANK FROM dual UNION ALL
            SELECT 'C' SHIPMENT, '1' ORDERID, 20 SHIP_QTY, 5 ORDER_QTY, 1500 RANK FROM dual UNION ALL
            SELECT 'C' SHIPMENT, '2' ORDERID, 20 SHIP_QTY, 5 ORDER_QTY, 1300 RANK FROM dual UNION ALL
            SELECT 'C' SHIPMENT, '3' ORDERID, 20 SHIP_QTY, 10 ORDER_QTY, 1000 RANK FROM dual UNION ALL
            SELECT 'C' SHIPMENT, '5' ORDERID, 20 SHIP_QTY, 20 ORDER_QTY, 900 RANK FROM dual UNION ALL
            SELECT 'C' SHIPMENT, '4' ORDERID, 20 SHIP_QTY, 20 ORDER_QTY, 600 RANK FROM dual 
        ),

创建两个 CTE 以获得预期结果:第一个只是获取 Shipments 的 ROWNUM。另一个(网格)准备数据以进行最终处理。

  SHIP_RNS AS   --  CTE SHIP_RNS (Row Numbers for Shipments)
    ( Select ROWNUM "SHIP_RN", SHIPMENT 
      From SHIPMENTS 
      Order By SHIPMENT 
    ),
  grid AS       --  CTE grid 
    ( SELECT *      --  (getting first leading rank, collecting ORDERIDs, separating first leading rank)
      FROM    ( Select      'IN LIST' "LIST_TP", SHIPMENT, SHIP_QTY, ORDERID, ORDER_QTY, RUN_TOT, OSR_RANK, 
                            LISTAGG(DISTINCT ORDERID, ',') WITHIN GROUP (Order By SHIPMENT, OSR_RANK Desc) Over(Partition By SHIPMENT) "ORD_LIST" 
                From        ( Select      SHIPMENT, ORDERID, SHIP_QTY, ORDER_QTY, "RANK" "OSR_RANK",
                                          Sum(1) OVER(Partition By SHIPMENT Order By SHIPMENT, "RANK" Desc) "ORD_RN",
                                          Sum(ORDER_QTY) OVER(Partition By SHIPMENT Order By SHIPMENT, "RANK" Desc) "RUN_TOT"
                              From        ORDER_SHIPMENT_RANK s
                            )
                Where       RUN_TOT <= SHIP_QTY 
             )
      Where       INSTR(ORD_LIST, ORDERID) > 0
  UNION ALL 
      SELECT *      --  Other data and ranks
      FROM    ( Select      'NOT IN LIST' "LIST_TP", SHIPMENT, SHIP_QTY, ORDERID, ORDER_QTY, RUN_TOT, OSR_RANK, 
                            LISTAGG(DISTINCT ORDERID, ',') WITHIN GROUP (Order By SHIPMENT, OSR_RANK Desc) Over(Partition By SHIPMENT) "ORD_LIST" 
                From        ( Select      SHIPMENT, ORDERID, SHIP_QTY, ORDER_QTY, "RANK" "OSR_RANK",
                                          Sum(1) OVER(Partition By SHIPMENT Order By SHIPMENT, "RANK" Desc) "ORD_RN",
                                          Sum(ORDER_QTY) OVER(Partition By SHIPMENT Order By SHIPMENT, "RANK" Desc) "RUN_TOT"
                              From        ORDER_SHIPMENT_RANK s
                            )
                Where       RUN_TOT > SHIP_QTY 
             )
      Where       INSTR(ORD_LIST, ORDERID) > 0
    )

CTE 网格将第一批货物与所有其他货物分开,并创建主代码中所需的一些新列(运行总计、ORDERID 列表、IN LIST 和 NOT IN LIST 分组......) 主要代码如下...

--  Main SQL
SELECT     SHIPMENT, ORDERID, ORDER_QTY
FROM      ( Select      sh.SHIP_RN, g.SHIPMENT, g.SHIP_QTY, g.ORDERID, g.ORDER_QTY, 
                        Sum(g.ORDER_QTY) OVER(Partition By g.SHIPMENT, g.LIST_TP Order By g.SHIPMENT, g.LIST_TP, g.OSR_RANK Desc) "RUN_TOT", 
                        g.OSR_RANK, g.ORD_LIST, g.LIST_TP
            From        grid g
            Inner Join  SHIP_RNS sh ON(sh.SHIPMENT = g.SHIPMENT)
          )
WHERE     RUN_TOT <= SHIP_QTY And 
          LIST_TP = Case When SHIP_RN = 1 Then 'IN LIST' Else 'NOT IN LIST' End And 
          InStr(ORD_LIST, ORDERID) > 0 

最终结果应如要求:

/*  R e s u l t : 
SHIPMENT ORDERID  ORDER_QTY
-------- ------- ----------
A              1          5
A              2          5
B              3         10
C              5         20     */


© www.soinside.com 2019 - 2024. All rights reserved.