Идентификаторы/значения группы, которые связаны друг с другом между несколькими столбцами

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

Кроме того, если мы получим новую строку, мы можем назначить правильный идентификатор с учетом того, что было сделано ранее для других идентификаторов группы.

Например:

Col1 Col2 Col3 Col4
AA Null 33 12
BB Null 45 12
AA 123 65 15
CC 123 NULL 42
DD Null 10 42
EE NULL 20 NULL
FF 145 33 NULL
GG NULL NULL 11

Желаемый результат:

Идентификатор группы = 1, потому что в столбце 1 это одно и то же значение для строк 1 и 3 (AA), а для строки 4 это также идентификатор 1, потому что во втором столбце значение для AA равно 123 (то же самое для CC). Если есть какие-либо соответствие между строками и пересечение столбцов, мы генерируем идентификатор

Col1 Col 2 Col 3 Col 4 Group ID
AA Null 33 12 1
BB Null 45 12 1
AA 123 65 15 1
CC 123 NULL 42 1
DD Null 10 42 1
EE NULL 20 NULL 2
FF 145 33 NULL 1
GG NULL NULL 11 3

person melazz    schedule 29.01.2021    source источник
comment
Как вы получаете этот столбец группировки? Я не вижу никакой общности. Кстати, пожалуйста, не публикуйте изображения данных, поместите их в свой пост.   -  person Andrew    schedule 29.01.2021
comment
Это лазурный синапс или сервер sql? Разрешает ли синапс общие табличные выражения?   -  person SteveC    schedule 29.01.2021
comment
@SteveC Я пытаюсь найти решение в лазурном и sql-сервере! но да .. мне это нужно для лазурного синапса!   -  person melazz    schedule 29.01.2021
comment
@Andrew спасибо за совет! извините я новенький! и я помещаю пояснение перед таблицей вывода, если оно понятно!   -  person melazz    schedule 29.01.2021
comment
Я не думаю, что это было бы возможно в одном операторе SQL. Вероятно, вам нужно написать скрипт (SQL или что-то другое), пройти несколько итераций. Поскольку в определении идентификатора группы нет логики как таковой, а правила для определения того, к какой группе принадлежит строка, довольно просты, но утомительны.   -  person Kashyap    schedule 29.01.2021
comment
Пара вопросов для вас - это может быть выполнимо в одном утверждении, но это во многом зависит от того, что вы пытаетесь сделать. Как вы планируете делать вставку, особенно для этой колонки? Вам нужно рассчитать столбец для существующих данных или просто вставляет? Являются ли дополнительные вставки одной или несколькими строками за раз (возможно, нам потребуется добавить более одного нового идентификатора?). Должны ли идентификаторы быть последовательными, или мы можем сгенерировать ROW_NUMBER и взять минимум для группы?   -  person SamaraSoucy    schedule 02.02.2021
comment
@SamaraSucy таблица построена путем объединения 3 других таблиц. после построения этой таблицы мы получим несколько строк после! Я не понимаю вопрос о вычислении столбца? если вы говорите об идентификаторе ... да, нам нужно сгенерировать идентификатор, и он не должен быть последовательным! ранг или номер строки могут быть возможны, но после группировки идентификаторов все они имеют один и тот же идентификатор!   -  person melazz    schedule 04.02.2021


Ответы (1)


Я немного поработал над этим и согласен с Кашьяпом - я не могу найти способ сделать это одним утверждением. Вам нужен либо рекурсивный CTE, либо цикл. В настоящее время Synapse не поддерживает рекурсивные CTE, что позволяет использовать цикл для создания желаемого эффекта.

Одна проблема, которая возникла, пока я работал с этим. По мере того, как вы продолжаете добавлять данные, у вас будет все больше и больше совпадений, и в конечном итоге вы можете остаться только с одной группой. Это зависит от вашего набора данных — у вас может быть что-то, что, как вы можете гарантировать, будет иметь дискретные подразделения. Как работает скрипт, который я собрал, новое совпадение обновит любые идентификаторы групп, даже в существующих данных. Вы можете изменить его, чтобы установить идентификаторы групп только для новых строк, но тогда вы можете оказаться в ситуации, когда одна строка соответствует нескольким группам.

Конечно, не единственный вариант, но это сценарий, который я собрал. Это зависит от наличия уникального идентификатора, который будет оставаться неизменным в каждой итерации. Поскольку цикл использует обновления вместо вставок, подготовка данных будет включать в себя вставку данных в вашу новую таблицу без группы, и вы можете создать свой идентификатор в это время, используя автоинкремент или иным образом. Сценарий лучше всего работает со столбцом INT ID, но при необходимости должен работать и с guid.

Итак, процесс по существу таков:

  1. Выполните любую начальную подготовку, необходимую для вставки данных в таблицу и создания идентификатора.
  2. Присоедините таблицу к самой себе, по одному разу для каждого столбца, который может содержать совпадение.
  3. Обновите идентификатор группы, чтобы он был минимальным значением среди идентификаторов и текущих идентификаторов групп этого набора совпадений.
  4. Проверьте, не нужно ли нам сделать еще один раунд. Поскольку мы используем минимальный идентификатор в качестве номера группы, будет строка, в которой ID = group ID в каждой группе
CREATE TABLE #testtable
  (
     [id]          INT NOT NULL,
     [col1]        INT NOT NULL,
     [col2]        INT NULL,
     [col3]        INT NULL,
     [groupnumber] INT NULL
  )

INSERT INTO #testtable
            (id,
             col1,
             col2,
             col3)
INSERT INTO #testTable (id, col1, col2, col3) 
    SELECT 1, 1, 5, 33 UNION ALL         -- First
    SELECT 2, 2, null, 45 UNION ALL      -- Second
    SELECT 3, 1, 123, 65 UNION ALL       -- First
    SELECT 4, 3, 123, null UNION ALL     -- First
    SELECT 5, 10, null, 10 UNION ALL      -- Third
    SELECT 6, 5, null, 45 UNION ALL      -- Second
    SELECT 7, 6, 145, 33                -- First


DECLARE @RemainingRows INT,
    @LoopCounter INT, @MaxLoops int             -- To protect against infinite loop
    
SET @RemainingRows = (SELECT COUNT([id]) FROM #testtable)
SET @LoopCounter = 0;
SET @MaxLoops = 10;
    
WHILE( @RemainingRows > 0 
    AND @LoopCounter < @MaxLoops ) 
BEGIN

    WITH combineddata AS 
    (
        SELECT
            id,
            col1,
            col2,
            col3,
            groupnumber
        FROM
            #testtable
    ),
    --Create a set a rows that contains all rows and all possible matches
    matcheddata AS 
    (
        SELECT
            c1.id,
            c1.col1 AS c1col1,
            c1.col2 AS c1col2,
            c1.col3 AS c1col3,
            c1.groupnumber AS groupNumber1,
            c2.id AS RowNum2,
            c2.groupnumber AS groupNumber2,
            c3.id AS RowNum3,
            c3.groupnumber AS groupNumber3,
            c4.id AS RowNum4,
            c4.groupnumber AS groupNumber4 
        FROM
            combineddata c1 
            LEFT JOIN
                combineddata c2 
                ON c1.col1 = c2.col1 
            LEFT JOIN
                combineddata c3 
                ON c1.col2 = c3.col2 
            LEFT JOIN
                combineddata c4 
                ON c1.col3 = c4.col3
    )
    UPDATE #testtable 
    SET
        groupnumber = 
            CASE
                WHEN
                    NEW.groupnumber IS NULL 
                THEN
                    NULL 
                ELSE
                    NEW.groupnumber 
            END
    FROM
        (
            SELECT
                id,
                c1col1,
                c1col2,
                c1col3,
                MIN(groupnumber) AS GroupNumber 
            FROM
                matcheddata CROSS apply (
                SELECT
                    MIN(c) AS GroupNumber 
                FROM (VALUES 
                    (id), 
                    (RowNum2), 
                    (RowNum3), 
                    (RowNum4),
                    (groupNumber1),
                    (groupNumber2),
                    (groupNumber3),
                    (groupNumber4)
                ) AS v (C)
                WHERE
                    c IS NOT NULL) g 
                GROUP BY
                    id,
                    c1col1,
                    c1col2,
                    c1col3
        ) NEW 
    INNER JOIN # testtable 
        ON NEW.id = #testtable.id 
        
    SET
        @LoopCounter = @LoopCounter + 1 
    SET
        @RemainingRows = 
        (
            SELECT
                COUNT(t1.id) 
            FROM
                #testtable t1 
                LEFT JOIN
                    #testtable t2 
                    ON t1.groupnumber = t2.[id] 
            WHERE
                t2.id IS NULL 
                OR t2.id <> t2.groupnumber
        )
        
        PRINT 'Remaining Rows: ' + CAST(@RemainingRows AS VARCHAR) PRINT 'Counter: ' + CAST(@LoopCounter AS VARCHAR);
END

SELECT * FROM #testtable 

IF Object_id('tempdb..#testTable') IS NOT NULL 
    BEGIN
        DROP TABLE # testtable 
    END```
person SamaraSoucy    schedule 09.02.2021