Перекрестное соединение неизвестных, но определенных N наборов данных во время выполнения - PullRequest
0 голосов
/ 07 мая 2019

Есть ли способ использовать оператор SQL для разделения наборов данных в таблице и последующего перекрестного соединения? В настоящее время я должен сделать это в C #, но это занимает много времени для обработки.

Для иллюстрации у меня есть тестовая таблица со следующими пунктами.

enter image description here

A1..A4, B1..B3, C1..C5 представляют 3 раздела, с которыми я имею дело. Обратите внимание, что разделы приведены только для иллюстрации. Я не знаю, сколько разделов в таблице, если мне не нужно их вручную группировать.

Я хотел бы сделать перекрестное объединение на всех 3 разделах (A, B, C), чтобы конечные результаты выглядели так:

enter image description here

Я знаю, что в какой-то момент нужно использовать рекурсию, но я не уверен, как к ней подойти.

Ответы [ 2 ]

1 голос
/ 09 мая 2019

Такого рода вещи могут быть выполнены с помощью рекурсивного запроса, хотя 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.
0 голосов
/ 08 мая 2019

это TSQL, но он может помочь вам создать нечто похожее в ORACLE.

WITH DATA_temp as (
    SELECT 'A1' ID UNION 
    SELECT 'A2' ID UNION 
    SELECT 'B1' ID UNION 
    SELECT 'B2' ID UNION 
    SELECT 'D1' ID UNION 
    SELECT 'D2' ID UNION 
    SELECT 'c1' ID UNION 
    SELECT 'c2' ID  
    ) 


    SELECT ID, DENSE_RANK()OVER(ORDER BY LEFT(ID,1)) AS RN
    INTO #TEMP
    FROM DATA_temp;

DECLARE 
    @X varchar(2) = '1'
    ,@TB varchar(2)
    ,@SQL AS VARCHAR(MAX)


WHILE @X <= (SELECT MAX(RN) FROM #TEMP)
BEGIN
SET @TB = (SELECT DISTINCT LEFT(ID,1) ID FROM #TEMP WHERE rn = @X)
SET @SQL = ISNULL(@SQL,'') + '(SELECT ID FROM #TEMP WHERE rn ='+ @X+' ) AS '+@TB+'  CROSS JOIN
'
SET @X = @X + 1
END 

SET @SQL = 'SELECT * FROM '+ LEFT(@SQL,LEN(@SQL) - 13)

EXEC(@SQL)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...