Такого рода вещи могут быть выполнены с помощью рекурсивного запроса, хотя 80+ уровней x-join являются довольно невообразимым количеством данных. Ниже я приведу пример, который генерирует разделенный запятыми набор строк, где каждая запись представляет один путь через перекрестные соединения.
Учитывая характер данных примера, в примере будут использоваться все начальные алфавитные символы в каждой записи в качестве раздела записи (это необходимо адаптировать, если реальные данные отличаются от образца примера).
Сначала создайте тестовые данные:
CREATE TABLE TEST_TABLE(ITEM VARCHAR2(32) NOT NULL PRIMARY KEY );
INSERT INTO TEST_TABLE VALUES ('A1');
INSERT INTO TEST_TABLE VALUES ('A2');
INSERT INTO TEST_TABLE VALUES ('A3');
INSERT INTO TEST_TABLE VALUES ('A4');
INSERT INTO TEST_TABLE VALUES ('B1');
INSERT INTO TEST_TABLE VALUES ('B2');
INSERT INTO TEST_TABLE VALUES ('B3');
INSERT INTO TEST_TABLE VALUES ('C1');
INSERT INTO TEST_TABLE VALUES ('C2');
INSERT INTO TEST_TABLE VALUES ('C3');
INSERT INTO TEST_TABLE VALUES ('C4');
INSERT INTO TEST_TABLE VALUES ('C5');
COMMIT;
Тогда запрос. На данный момент ожидается 60 строк, поскольку в группах «A», «B», «C» имеется 4 * 3 * 5 записей.
Механически приведенный ниже пример разделит записи по алфавитному префиксу (в предоставленных данных на разделы 'A', 'B', 'C').
Затем он (произвольно) начнет с (в алфавитном порядке) первого раздела (в данном случае «A», хотя он определяется динамически) и рекурсивно соединит каждую запись в этом разделе со всеми записями в (алфавитном порядке) следующего раздела (' B 'в этом случае), создавая пути с двумя узлами и умножая количество записей, как это происходит.
Затем этот же процесс повторяется для каждого двухузлового пути в следующий раздел (в данном случае 'C'), где разделы исчерпаны и он останавливается. Тогда принимаются только те пути, которые представляют полное дерево от корня до листа (три узла в этом примере).
WITH PARTITIONED_RECORD AS (
SELECT ITEM,
DENSE_RANK() OVER (ORDER BY REGEXP_SUBSTR(ITEM, '^[A-Z]{1,}') ASC) AS PARTITION_SORT,
ROW_NUMBER() OVER (ORDER BY ITEM ASC NULLS LAST) AS ABSOLUTE_SORT
FROM TEST_TABLE),
RAW_GRAPH AS (
SELECT SYS_CONNECT_BY_PATH(ITEM,',' ) AS CONNECTION_PATH,
CONNECT_BY_ISLEAF AS LEAF_MARKER
FROM PARTITIONED_RECORD
START WITH PARTITION_SORT = 1
CONNECT BY PARTITION_SORT = PRIOR PARTITION_SORT + 1
AND ABSOLUTE_SORT > PRIOR ABSOLUTE_SORT)
SELECT REGEXP_REPLACE(CONNECTION_PATH,'^,',NULL) AS CROSS_JOINED_SET
FROM RAW_GRAPH
WHERE LEAF_MARKER = 1
ORDER BY 1 ASC;
Результат:
CROSS_JOINED_SET
A1,B1,C1
A1,B1,C2
A1,B1,C3
A1,B1,C4
A1,B1,C5
A1,B2,C1
...
A4,B3,C3
A4,B3,C4
A4,B3,C5
60 rows selected.
Затем протестируйте дополнительные разделы. Во-первых, добавление одного элемента в новый паритет.
INSERT INTO TEST_TABLE VALUES ('D1');
И повторный запрос (ожидается 60 строк, поскольку в x-join нет расширения):
CROSS_JOINED_SET
...
A4,B3,C2,D1
A4,B3,C3,D1
A4,B3,C4,D1
A4,B3,C5,D1
60 rows selected.
И добавьте второй элемент в раздел «D» и повторите запрос (теперь ожидается 120 записей):
INSERT INTO TEST_TABLE VALUES ('D2');
Результат:
CROSS_JOINED_SET
A1,B1,C1,D1
A1,B1,C1,D2
A1,B1,C2,D1
A1,B1,C2,D2
...
A4,B3,C4,D1
A4,B3,C4,D2
A4,B3,C5,D1
A4,B3,C5,D2
120 rows selected.
это
И добавлена пятая группа с двумя записями (ожидается 240 строк):
INSERT INTO TEST_TABLE VALUES ('EJY1017');
INSERT INTO TEST_TABLE VALUES ('EJY1018');
Результат:
CROSS_JOINED_SET
A1,B1,C1,D1,EJY1017
A1,B1,C1,D1,EJY1018
A1,B1,C1,D2,EJY1017
A1,B1,C1,D2,EJY1018
...
A4,B3,C5,D1,EJY1017
A4,B3,C5,D1,EJY1018
A4,B3,C5,D2,EJY1017
A4,B3,C5,D2,EJY1018
240 rows selected.
РЕДАКТИРОВАТЬ: добавление еще одного варианта, который использует динамические перекрестные соединения.
Другой подход заключается в создании x-соединений в динамическом sql. Приведенный ниже пример снова возвращает строки, разделенные запятыми, но делает это с помощью операторов CROSS JOIN
.
- Создать тип возвращаемого значения (не обязательно)
CREATE OR REPLACE TYPE STRINGS IS TABLE OF VARCHAR2(4000);
/
- И функция, которая обнаруживает различные группы и создает перекрестные соединения для каждой.
CREATE OR REPLACE FUNCTION XJOIN RETURN STRINGS
IS
V_HEADER CHARACTER VARYING(512 BYTE) := ' SELECT ';
V_XJOINS CHARACTER VARYING(32000 BYTE) := NULL;
V_BUCKETS STRINGS := STRINGS();
V_RESULTS STRINGS := STRINGS();
BEGIN
SELECT DISTINCT REGEXP_SUBSTR(ITEM,'^[A-Z]{1,}')
BULK COLLECT INTO V_BUCKETS
FROM TEST_TABLE ORDER BY 1 ASC;
FOR BUCKET_INDEX IN 1..V_BUCKETS.COUNT
LOOP
IF BUCKET_INDEX > 1 THEN
V_HEADER := V_HEADER||'||CHR(44)||';
V_XJOINS := V_XJOINS||' CROSS JOIN ';
END IF;
V_HEADER := V_HEADER||V_BUCKETS(BUCKET_INDEX);
V_XJOINS := V_XJOINS || UTL_LMS.FORMAT_MESSAGE(Q'! (SELECT ITEM AS %s FROM TEST_TABLE WHERE ITEM LIKE '%s%') !',V_BUCKETS(BUCKET_INDEX),V_BUCKETS(BUCKET_INDEX));
END LOOP;
EXECUTE IMMEDIATE (V_HEADER||' FROM '||V_XJOINS) BULK COLLECT INTO V_RESULTS;
RETURN V_RESULTS;
END;
/
- И назовите это:
SELECT COLUMN_VALUE FROM TABLE ( XJOIN());
...
A2,B3,C5,D2,EJY1018
A3,B3,C5,D2,EJY1018
A4,B3,C5,D2,EJY1018
240 rows selected.