Oracle SQL中项目的高级分组

时间:2011-01-06 10:27:38

标签: sql oracle

我有一个有趣的问题。我必须根据它们是否包装在同一组容器中,为一组订单分配一个ID。一个订单可能位于一个或多个容器中,这意味着并非组中的所有容器都包含所有订单。例如,给定这些订单:

 ORDER1 is in container A and B
 ORDER2 is in container B and C
 ORDER3 is in container C and D
 ORDER4 is in container E

应该有两个组,第一组包含ORDER1,ORDER2和ORDER3,第二组只包含ORDER4。请注意,ORDER1和ORDER3不共享任何容器。

我可以想到一个相当简单的程序算法来进行这种分组 - 尽管正确的细节可能会有点痛苦。

但是,如果可能的话,我希望有一个基于SQL的解决方案,但它超出了我的掌握范围。我正在使用Oracle 10.2 - 我猜这里可能会有一些时髦的功能。

1 个答案:

答案 0 :(得分:1)

这是一个有趣的问题,类似于this SO。您可以按照相同的方法构建查询:

SQL> WITH orders AS (
  2     SELECT 'ORDER1' ord, 'A' cont FROM dual
  3     UNION ALL SELECT 'ORDER1' , 'B' FROM dual
  4     UNION ALL SELECT 'ORDER2' , 'B' FROM dual
  5     UNION ALL SELECT 'ORDER2' , 'C' FROM dual
  6     UNION ALL SELECT 'ORDER3' , 'C' FROM dual
  7     UNION ALL SELECT 'ORDER3' , 'D' FROM dual
  8     UNION ALL SELECT 'ORDER4' , 'E' FROM dual
  9  )
 10  SELECT ord, MIN(grp) "group" /*, cont*/
 11    FROM (SELECT connect_by_root(ord) ord,
 12                 connect_by_root(cont) cont,
 13                 cont grp
 14             FROM orders
 15           CONNECT BY NOCYCLE(cont = PRIOR cont
 16                           OR ord = PRIOR ord))
 17  GROUP BY ord /*, cont*/
 18  ORDER BY ord, MIN(grp);

ORD    group
------ -----
ORDER1 A
ORDER2 A
ORDER3 A
ORDER4 E

更新

我尝试生成更多数据来重现您的性能问题。只有一千个订单,查询确实没有及时返回。

我尝试使用CONNECT BY和START WITH子句调整查询,但无法提高性能。我的下一个想法是在更传统的分层视图中显示数据:

SQL> SELECT o1.ord "order", o2.ord "is connected to"
  2    FROM orders o1
  3    JOIN orders o2 ON o1.cont = o2.cont
  4                  AND o1.ord < o2.ord;

order  is connected to
------ ---------------
ORDER1 ORDER2
ORDER2 ORDER3

这反过来是以下查询的基础,它在我的测试数据集上做得很好:

SQL> SELECT o.ord, nvl(MIN(connexions.grp), o.ord) grp
  2    FROM orders o
  3    LEFT JOIN (SELECT connect_by_root(ord1) grp, ord2
  4                      --, sys_connect_by_path(ord1, '->')
  5                 FROM (SELECT o1.ord ord1, o2.ord ord2
  6                          FROM orders o1
  7                          JOIN orders o2 ON o1.cont = o2.cont
  8                                        AND o1.ord < o2.ord)
  9               CONNECT BY PRIOR ord2 = ord1
 10                ORDER BY 1, 2) connexions ON o.ord = connexions.ord2
 11    GROUP BY o.ord
 12    order by 1,2;

ORD    GRP
------ ------
ORDER1 ORDER1
ORDER2 ORDER1
ORDER3 ORDER1
ORDER4 ORDER4

我使用以下查询来填充我的数据集(1200行):

CREATE TABLE orders AS
SELECT 'ORDER' || to_char(dbms_random.VALUE(0, 1000), 'fm000000') ord,
       to_char(dbms_random.VALUE(0, 800), 'fm000000') cont
  FROM dual
CONNECT BY LEVEL <= 1200;