Расширенная группировка элементов в Oracle SQL - PullRequest
1 голос
/ 06 января 2011

У меня интересная проблема.Я должен назначить идентификатор группе заказов, основываясь на том, упакованы ли они в одну группу контейнеров или нет.Один заказ может быть в одном или нескольких контейнерах, что означает, что не все контейнеры в группе содержат все заказы.Например, с учетом этих заказов:

 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 Ответ

1 голос
/ 06 января 2011

Это интересный вопрос, похожий на этот 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...