2015-02-17 6 views
9

Как объединить несколько строк с одинаковыми ID в одну строку.Объединить несколько строк с одинаковым идентификатором в одну строку

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

У меня есть таблица:

ID |A |B |C 
1 NULL 31 NULL 
1 412 NULL 1 
2 567 38 4 
2 567 NULL NULL 
3 2  NULL NULL 
3 5  NULL NULL 
4 6  1  NULL 
4 8  NULL 5 
4 NULL NULL 5 

Я хочу, чтобы получить таблицу:

ID |A |B |C 
1 412 31 1 
2 567 38 4 
3 2  NULL NULL 
3 5  NULL NULL 
4 6  1  NULL 
4 8  NULL 5 
4 NULL NULL 5 
+3

что вы пробовали? Что произойдет, если у вас более двух строк с разными данными в двух строках? Как вы выбираете, какую строку взять данные из другого? – Kritner

+0

Почему в вашем желаемом результате вы сохраняете последнюю строку ('4 NULL NULL 5')? Не следует ли слиться с предыдущей строкой ('4 8 NULL 5')? –

+0

@VladimirBaranov Нет, его не следует сливать из-за строки ('4 6 1 NULL'), имеющей конфликт во втором значении' 6' с '8' в следующей строке. Согласно приведенному объяснению, никакие строки для этого идентификатора не должны быть объединены. – ErikE

ответ

5

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

WITH CTE AS (
    SELECT 
     ID, 
     MAX(A) AS A, 
     MAX(B) AS B, 
     MAX(C) AS C 
    FROM dbo.Records 
    GROUP BY ID 
    HAVING MAX(A) = MIN(A) 
     AND MAX(B) = MIN(B) 
     AND MAX(C) = MIN(C) 
) 
    SELECT * 
    FROM CTE 
    UNION ALL 
    SELECT * 
    FROM dbo.Records 
    WHERE ID NOT IN (SELECT ID FROM CTE) 

SQL Скрипки: http://www.sqlfiddle.com/#!6/29407/1/0

+0

Хорошая работа, видя более простой способ. Для чего это стоит, 'НЕ В 'дает мне завещания. «НЕ СУЩЕСТВУЕТ» кажется лучше. :) – ErikE

+0

Пока 'NOT IN' использует поле, которое не является нулевым, производительность будет идентичной. В этом случае, делая это на первичном ключе, мы в безопасности. Оба создают LEFT ANTI SEMI JOIN оптимизатором. Я бы определенно избегал этого, если я знаю, что поле имеет нули, а на практике - в поле, не проиндексированном или varchars. –

+0

Я только что видел снова и снова, когда использование 'NOT IN' приводит разработчиков к разному виду мышления, чем' JOIN' или 'EXISTS', и в конечном итоге этот тип мышления не такой легкий и не имеет никакого значения, t производит как хорошо структурированные запросы (и может в конечном итоге иметь проблемы с производительностью, если движок не конвертирует его в соединение). – ErikE

0

Вы можете попробовать что-то вроде этого:

select 
isnull(t1.A, t2.A) as A, 
isnull(t1.B, t2.B) as B, 
isnull(t1.C, t2.C) as C 
from 
table_name t1 
join table_name t2 on t1.ID = t2.ID and ..... 

Вы упоминаете понятия первой и второй , How do
Вы определяете этот заказ? Поместите этот порядок, определяющий условие
здесь: .....

Кроме того, предположим, у вас ровно 2 строки для каждого значения идентификатора.

+1

COALESCE будет лучше ISNULL здесь – Codeman

+0

@ Pheonixblade9 Это может быть, да. Но я сомневаюсь, что это главное. –

+0

Yupper, вот почему я сделал комментарий, а не добавил свой собственный ответ - вы поняли это правильно, я просто думаю, что COALESCE лучше. – Codeman

3
WITH Collapsed AS (
    SELECT 
     ID, 
     A = Min(A), 
     B = Min(B), 
     C = Min(C) 
    FROM 
     dbo.MyTable 
    GROUP BY 
     ID 
    HAVING 
     EXISTS (
     SELECT Min(A), Min(B), Min(C) 
     INTERSECT 
     SELECT Max(A), Max(B), Max(C) 
    ) 
) 
SELECT 
    * 
FROM 
    Collapsed 
UNION ALL 
SELECT 
    * 
FROM 
    dbo.MyTable T 
WHERE 
    NOT EXISTS (
     SELECT * 
     FROM Collapsed C 
     WHERE T.ID = C.ID 
); 

See this working in a SQL Fiddle

Это работает, создавая все объединяемые строки посредством использования Min и Max --which должны быть одинаковыми для каждого столбца в пределах ID и которые целесообразно исключить NULL с - затем добавления в этот список перечислены все строки из таблицы, которые не могут быть объединены. Специальный трюк с EXISTS ... INTERSECT допускает случай, когда столбец имеет все значения NULL для ID (и, таким образом, Min и - NULL и не могут равняться друг другу). То есть, он функционирует как Min(A) = Max(A) AND Min(B) = Max(B) AND Min(C) = Max(C), но позволяет NULL s для сравнения.

Это немного другое (ранее) решение, которое я дал, которое может предлагать различные характеристики производительности, и быть более сложным, мне нравится меньше, но, будучи единственным текущим запросом (без UNION), я вроде как больше.

WITH Collapsible AS (
    SELECT 
     ID 
    FROM 
     dbo.MyTable 
    GROUP BY 
     ID 
    HAVING 
     EXISTS (
     SELECT Min(A), Min(B), Min(C) 
     INTERSECT 
     SELECT Max(A), Max(B), Max(C) 
    ) 
), Calc AS (
    SELECT 
     T.*, 
     Grp = Coalesce(C.ID, Row_Number() OVER (PARTITION BY T.ID ORDER BY (SELECT 1))) 
    FROM 
     dbo.MyTable T 
     LEFT JOIN Collapsible C 
     ON T.ID = C.ID 
) 
SELECT 
    ID, 
    A = Min(A), 
    B = Min(B), 
    C = Min(C) 
FROM 
    Calc 
GROUP BY 
    ID, 
    Grp 
; 

Это также в вышеупомянутом скрипте SQL.

Это использует подобную логику первого запроса, чтобы вычислить, должна ли быть объединена группа, а затем использует это, чтобы создать ключ группировки, которая либо одинаковы для всех строк в пределах ID или различно для всех строк в пределах ID. С окончательным Min (будет работать так же хорошо), строки, которые должны быть объединены, объединяются, потому что они разделяют ключ группировки, а строки, которые не должны объединяться, не потому, что у них есть отдельные ключи группировки по ID.

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

+0

Не просматривали все это, но как 'HAVING Min (A) = Max (A) AND Min (B) = Max (B) AND Min (C) = Max (C)', я бы, вероятно, заменил его с «ИСПРАВНОСТЬЮ» (ВЫБРАТЬ Мин (A), Мин (B), Мин (C) ИНТЕРЕСНЫЙ ВЫБРАТЬ Макс (A), Макс (B), Макс (C)) 'из-за таких случаев, как' 1, 2, NULL; 1, NULL, NULL' (т. Е. Где по крайней мере один столбец равен NULL для всех сворачиваемых строк). –

+0

Вы правы в столбце all -NULL для одного идентификатора. Обновлено ли мое обновление (см. Скрипт SQL)? Я также рассмотрел «НЕ СУЩЕСТВУЕТ» (SELECT Min (A), Min (B), Min (C) EXCEPT SELECT Max (A), Max (B), Max (C)) '- у вас есть комментарии по этому поводу для меня (Любопытно, если я пропускаю что-то там, где он не будет работать одинаково). – ErikE

+0

Теперь все кажется прекрасным. Я думал о том, как избежать возможного повторения оценки «Складной» и пытался заменить не сворачиваемую часть вашего запроса, но в конце концов я возьму на себя это, для чего потребуется UNION вместо UNION ALL. Ну, я думаю, это так хорошо, как может быть. :) Хорошая работа в любом случае. –

Смежные вопросы