身份证 | 金额 | 品牌 |
---|---|---|
1 | 10 | 空 |
1 | 20 | 空 |
2 | 30 | 马扎达 |
2 | 空 | 宝马 |
3 | 40 | 空 |
3 | 40 | 起亚 |
4 | 空 | 本田 |
4 | 空 | 本田 |
上面是原始表格,我的目标是找到每列相同 ID 内的任何差异。
对于每个 ID 组,如果值列有任何差异,则返回具有列名称的新行。
结果应该是这样的:
身份证 | 差异 |
---|---|
1 | 金额 |
2 | 金额 |
2 | 品牌 |
3 | 品牌 |
我的 PostgreSQL 代码:
SELECT
ID,
'Amount' AS Difference
FROM
table
GROUP BY
ID
HAVING
COUNT(DISTINCT amount) > 1
OR (COUNT(amount) != COUNT(*) AND COUNT(DISTINCT amount) > 0)
UNION ALL
SELECT
ID,
'Brand' AS Difference
FROM
table
GROUP BY
ID
HAVING
COUNT(DISTINCT brand) > 1
OR (COUNT(brand) != COUNT(*) AND COUNT(DISTINCT brand) > 0)
注意记录中存在NULL值,这样聚合函数就不会统计NULL值。 NULL 与 NULL 视为相同,但 NULL 与任何值视为不同。
此解决方案是否有更好或更快的查询?喜欢横向连接或窗口函数吗?
在桌子上单次通过应该会快很多:
SELECT id, col
FROM (
SELECT id
, min(amount) <> max(amount) AS a_diff, bool_or(true) FILTER (WHERE amount IS NULL) AS a_null
, min(brand) <> max(brand) AS b_diff, bool_or(true) FILTER (WHERE brand IS NULL) AS b_null
FROM tbl
GROUP BY id
) t
JOIN LATERAL (
VALUES
('Amount', a_diff, a_null)
, ('Brand' , b_diff, b_null)
) x(col, diff, has_null) ON (diff OR NOT diff AND has_null)
ORDER BY id, col
另外,
count(DISTINCT ...)
是出了名的昂贵。我们实际上并不需要那个计数。我们只需要知道最小值是否与最大值不同(或不同,但至少还有一个NULL
)。
非常相似的案例,有更多解释:
看起来您已经看过那个,但尚未应用于您的案例。
就像我在那里提到的:对于每组many行有更快的方法......