我有一个有趣的问题。我必须根据它们是否包装在同一组容器中,为一组订单分配一个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 - 我猜这里可能会有一些时髦的功能。
答案 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;