Генерация двухуровневой иерархии из набора данных с несколькими общими полями группировки в SQL - PullRequest
0 голосов
/ 10 февраля 2010

Моя база данных - MS SQL 2008.

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

Таблица

ID     Name     Code
1      Ben      1
2      Ben      1
3      Frank    1
4      Frank    2
5      Mark     2
6      Mary     3
7      Chuck    3
8      Rogue    10
9      Charles  11

Данные расположены не по порядку, значение «Родитель» не имеет значения, если оно имеется в каждой группе. Дочерние элементы родителя - это набор записей, связанных одинаковым именем, одним и тем же кодом или обоими. Каждая запись может появиться только один раз в IE результата. не может принадлежать более чем одной группе.

Вот один из возможных результатов (иерархия не должна быть представлена ​​таким образом):

ID     Name     Code     ParentID
1      Ben      1        NULL
2      Ben      1        1
3      Frank    1        1
4      Frank    2        1
5      Mark     2        1
6      Mary     3        NULL
7      Chuck    3        6

Идентификатор записи {1} является родителем группы 1 (выбран из-за первого общего набора)

{2} имеет то же имя, поэтому оно включено (также может быть включено из-за того же кода)

{3} использует тот же код, поэтому он включен

{4} имеет то же имя, что и {3}, поэтому оно включено

{5} делит тот же код с {4}, поэтому он включен

{6} и {7} используют один и тот же код, поэтому создайте новую группу.

{8} и {9} исключены из результата, так как других общих записей нет.

Я думаю, что я нашел решение в работе, которое использует приблизительно 3 или 4 соединения этой таблицы на себя, и это довольно запутанно. Любые предложения о том, как справиться с этим? Я чувствую, может быть, использование рекурсивных CTE, но я не могу обернуть мой мозг вокруг него.

1 Ответ

0 голосов
/ 10 февраля 2010

Я не думаю, что здесь будет работать рекурсивный CTE.Запрос полностью основан на последовательной логике, и нет никакого логического «следующего набора» для любого данного состояния, потому что вы не можете знать, где находится точка остановки, без предварительного сканирования каждой строки один за другим;другими словами, результаты по существу должны оцениваться построчно.Целью использования рекурсивного CTE является добавление sets ;если вы просто добавляете строки , то в итоге получается не лучше, чем курсор.

Я бы на самом деле использовал определяемый пользователем агрегат CLR для чего-то подобного, потому что естья не могу придумать решения с чистым SQL с хорошей производительностью, но если вам нужно решение с чистым SQL, вот одно, использующее обычные (не рекурсивные) CTE и функции управления окнами:

;WITH Rows_CTE AS
(
    SELECT
        ID, Name, Code,
        ROW_NUMBER() OVER (ORDER BY ID) AS RowNum
    FROM @Tbl
),
Changes_CTE AS
(
    SELECT
        r1.ID, r1.Name, r1.Code,
        CASE
            WHEN r1.Name = r2.Name OR r1.Code = r2.Code THEN NULL
            ELSE r1.ID
        END AS BeginGroupID
    FROM Rows_CTE r1
    LEFT JOIN Rows_CTE r2
        ON r2.RowNum = r1.RowNum - 1
),
Groups_CTE AS
(
    SELECT ID, Name, Code, BeginGroupID, m.EndGroupID
    FROM Changes_CTE c1
    CROSS APPLY
    (
        SELECT MIN(ID) AS EndGroupID
        FROM Changes_CTE c2
        WHERE c2.ID > c1.BeginGroupID
        AND c2.BeginGroupID IS NOT NULL
    ) m
)
SELECT
    t.*,
    CASE
        WHEN t.ID = g.BeginGroupID THEN NULL
        ELSE g.BeginGroupID
    END AS ParentID
FROM Groups_CTE g
INNER JOIN @Tbl t
    ON t.ID >= g.BeginGroupID
    AND t.ID < g.EndGroupID

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

(Приложение: мы могли бы использовать рекурсивный CTE и значительно улучшить его, если бы оно было известно наНачиная с того, что каждое имя / код может указывать только на одного «родителя», но это предположение нигде не задокументировано, поэтому нам действительно приходится предполагать наихудший случай.)

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