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
);
该方法的实现方式是通过使用Min
和Max
创建所有可合并的行--对于每个ID
中的每一列,它们应该相同,并且有用地排除了NULL
值--然后将无法合并的行附加到该列表中。对于当一个列对于一个ID
具有全部的NULL
值(因此 Min
和 Max
都为 NULL
无法相等) 的情况,特殊技巧是使用EXISTS ... INTERSECT
来实现, 它将功能视为 Min(A) = Max(A) AND Min(B) = Max(B) AND Min(C) = Max(C)
,但允许将NULL
比较为相等。
下面是我早些时候提供的稍微不同的解决方案,可能具有不同的性能特征,而且更复杂,我不太喜欢,但是由于它是一个单一连续的查询(没有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 Fiddle中。
这个查询与第一个查询使用类似的逻辑来计算一个组是否应该合并,然后使用此逻辑创建一个分组键,该键对于ID
内的所有行都相同,或者对于ID
内的所有行都不同。通过最终的Min
(Max
同样有效),共享分组键的行将被合并,因为它们具有相同的分组键,而具有不同分组键的行不会被合并,因为它们在ID
上具有不同的分组键。
根据您的数据集、索引、表大小和其他性能因素,这两个查询中的任何一个都可能表现更好,尽管第二个查询需要做一些工作才能追赶上第一个查询,因为它需要进行两次排序而非一次。
4 NULL NULL 5
)?难道它不应该与前一行(4 8 NULL 5
)合并吗? - Vladimir Baranov4 6 1 NULL
)的第二个值6
与下一行中的8
冲突。根据所给的解释,那么该ID的任何行都不应该被合并。 - ErikE