10

在 oracle 中,该函数允许我通过子句LISTAGG分析地使用它。OVER (PARTITION BY column..)但是,它不支持使用带有ROWSorRANGE关键字的窗口。

我有一个来自存储寄存器的数据集(针对问题进行了简化)。请注意,登记表的数量始终为 1 - 一项,一项交易行。

TranID TranLine ItemId OrderID Dollars Quantity
------ -------- ------ ------- ------- --------
1      101      23845  23      2.99    1
1      102      23845  23      2.99    1
1      103      23845  23      2.99    1
1      104      23845  23      2.99    1
1      105      23845  23      2.99    1

我必须将此数据“匹配”到特殊订单系统中的表,其中项目按数量分组。请注意,系统可以在多行上具有相同的项目 ID(即使项目相同,订购的组件也可能不同)。

ItemId OrderID Order Line Dollars Quantity
------ ------- ---------- ------- --------
23845  23      1          8.97    3
23845  23      2          5.98    2

我可以匹配此数据的唯一方法是通过订单 ID、项目 ID 和美元金额。

基本上我需要得到以下结果。

ItemId OrderID Order Line Dollars Quantity Tran ID  Tran Lines
------ ------- ---------- ------- -------- -------  ----------
23845  23      1          8.97    3        1        101;102;103
23845  23      2          5.98    2        1        104;105

我并不特别关心 tran 行是否以任何方式排序,我只关心美元金额是否匹配,并且我不会“重复使用”寄存器中的一行来计算特殊订单的总数。我不需要将 tran 行分解为表格 - 这是出于报告目的,并且粒度永远不会回到注册事务行级别。

我最初的想法是,我可以使用分析函数来执行此操作,以进行“最佳匹配”,以识别与订购系统中的美元金额和数量匹配的第一组行,从而为我提供如下结果集:

TranID TranLine ItemId OrderID Dollars Quantity CumDollar  CumQty
------ -------- ------ ------- ------- -------- --------   ------
1      101      23845  23      2.99    1        2.99       1
1      102      23845  23      2.99    1        5.98       2
1      103      23845  23      2.99    1        8.97       3
1      104      23845  23      2.99    1        11.96      4
1      105      23845  23      2.99    1        14.95      5

到现在为止还挺好。但我随后尝试将 LISTAGG 添加到我的查询中:

SELECT tranid, tranline, itemid, orderid, dollars, quantity, 
       SUM(dollars) OVER (partition by tranid, itemid, orderid order by tranline) cumdollar,
       SUM(quantity) OVER (partition by tranid, itemid, orderid order by tranline) cumqty
       LISTAGG (tranline) within group (order by tranid, itemid, orderid, tranline) OVER (partition by tranid, itemid, orderid)
FROM table

我发现它总是返回一个完整的 agg 而不是累积的 agg:

TranID TranLine ItemId OrderID Dollars Quantity CumDollar  CumQty ListAgg
------ -------- ------ ------- ------- -------- --------   ------ -------
1      101      23845  23      2.99    1        2.99       1      101;102;103;104;105
1      102      23845  23      2.99    1        5.98       2      101;102;103;104;105
1      103      23845  23      2.99    1        8.97       3      101;102;103;104;105
1      104      23845  23      2.99    1        11.96      4      101;102;103;104;105
1      105      23845  23      2.99    1        14.95      5      101;102;103;104;105

所以这没什么用。

如果可能的话,我更愿意在 SQL 中执行此操作。我知道我可以用游标和程序逻辑来做到这一点。

有没有办法用 LISTAGG 分析函数进行窗口化,或者可能是另一个支持这个的分析函数?

我在 11gR2 上。

4

2 回答 2

7

我能想到的唯一方法是使用相关子查询:

WITH CTE AS
(   SELECT  TranID, 
            TranLine, 
            ItemID, 
            OrderID, 
            Dollars, 
            Quantity, 
            SUM(dollars) OVER (PARTITION BY TranID, ItemID, OrderID ORDER BY TranLine) AS CumDollar, 
            SUM(Quantity) OVER (PARTITION BY TranID, ItemID, OrderID ORDER BY TranLine) AS CumQuantity
    FROM    T
)
SELECT  TranID, 
        TranLine, 
        ItemID, 
        OrderID, 
        Dollars, 
        Quantity, 
        CumDollar, 
        CumQuantity, 
        (   SELECT  LISTAGG(Tranline, ';') WITHIN GROUP(ORDER BY CumQuantity)
            FROM    CTE T2
            WHERE   T1.CumQuantity >= T2.CumQuantity
            AND     T1.ItemID = T2.ItemID
            AND     T1.OrderID = T2.OrderID
            AND     T1.TranID = T2.TranID
            GROUP BY tranid, itemid, orderid
        ) AS ListAgg
FROM    CTE T1;

我意识到这并没有给出你所要求的确切输出,但希望它足以克服累积 LISTAGG 的问题并让你继续前进。

我已经设置了一个SQL Fiddle来演示该解决方案。

于 2012-06-08T19:36:19.523 回答
2

在您的示例中,您的商店登记表包含 5 行,您的特殊订单系统表包含 2 行。您的预期结果集包含您的特殊订单系统表中的两行,并且应在“Tran Line”列中提及您的商店寄存器表的所有“tranlines”。

这意味着您需要将这 5 行聚合为 2 行。这意味着您不需要 LISTAGG 分析函数,但需要 LISTAGG 聚合函数。

您的挑战是将商店寄存器表的行连接到特殊订单系统表中的右侧行。通过计算美元和数量的运行总和,您的进展顺利。唯一缺少的步骤是定义美元和数量的范围,您可以通过这些范围将每个商店登记行分配给每个特殊订单系统行。

这是一个例子。首先定义表:

SQL> create table store_register_table (tranid,tranline,itemid,orderid,dollars,quantity)
  2  as
  3  select 1, 101, 23845, 23, 2.99, 1 from dual union all
  4  select 1, 102, 23845, 23, 2.99, 1 from dual union all
  5  select 1, 103, 23845, 23, 2.99, 1 from dual union all
  6  select 1, 104, 23845, 23, 2.99, 1 from dual union all
  7  select 1, 105, 23845, 23, 2.99, 1 from dual
  8  /

Table created.

SQL> create table special_order_system_table (itemid,orderid,order_line,dollars,quantity)
  2  as
  3  select 23845, 23, 1, 8.97, 3 from dual union all
  4  select 23845, 23, 2, 5.98, 2 from dual
  5  /

Table created.

和查询:

SQL> with t as
  2  ( select tranid
  3         , tranline
  4         , itemid
  5         , orderid
  6         , sum(dollars) over (partition by itemid,orderid order by tranline) running_sum_dollars
  7         , sum(quantity) over (partition by itemid,orderid order by tranline) running_sum_quantity
  8      from store_register_table srt
  9  )
 10  , t2 as
 11  ( select itemid
 12         , orderid
 13         , order_line
 14         , dollars
 15         , quantity
 16         , sum(dollars) over (partition by itemid,orderid order by order_line) running_sum_dollars
 17         , sum(quantity) over (partition by itemid,orderid order by order_line) running_sum_quantity
 18      from special_order_system_table
 19  )
 20  , t3 as
 21  ( select itemid
 22         , orderid
 23         , order_line
 24         , dollars
 25         , quantity
 26         , 1 + lag(running_sum_dollars,1,0) over (partition by itemid,orderid order by order_line) begin_sum_dollars
 27         , running_sum_dollars end_sum_dollars
 28         , 1 + lag(running_sum_quantity,1,0) over (partition by itemid,orderid order by order_line) begin_sum_quantity
 29         , running_sum_quantity end_sum_quantity
 30      from t2
 31  )
 32  select t3.itemid "ItemID"
 33       , t3.orderid "OrderID"
 34       , t3.order_line "Order Line"
 35       , t3.dollars "Dollars"
 36       , t3.quantity "Quantity"
 37       , t.tranid "Tran ID"
 38       , listagg(t.tranline,';') within group (order by t3.itemid,t3.orderid) "Tran Lines"
 39    from t3
 40         inner join t
 41           on (   t.itemid = t3.itemid
 42              and t.orderid = t3.orderid
 43              and t.running_sum_dollars between t3.begin_sum_dollars and t3.end_sum_dollars
 44              and t.running_sum_quantity between t3.begin_sum_quantity and t3.end_sum_quantity
 45              )
 46   group by t3.itemid
 47       , t3.orderid
 48       , t3.order_line
 49       , t3.dollars
 50       , t3.quantity
 51       , t.tranid
 52  /

    ItemID    OrderID Order Line    Dollars   Quantity    Tran ID Tran Lines
---------- ---------- ---------- ---------- ---------- ---------- --------------------
     23845         23          1       8.97          3          1 101;102;103
     23845         23          2       5.98          2          1 104;105

2 rows selected.

问候,
罗布。

于 2012-06-10T09:59:04.730 回答