SQL 在某些条件下分组
SQL group by under some conditions
我有一个很大的 table,其中有大量重复的行(在我关心的那些列中)。让我从以下示例开始:
|field1 | field2| field3| field4| field5|
| aa | 1 | NULL | 1 | 0 |
| aaa | 1 | NULL | 1 | 1 |
| aaa | 1 | NULL | 1 | 2 |
| a | 2 | 0 | 1 | 3 |
| a | 2 | 0 | NULL | 4 |
| a | 2 | NULL | 2 | 5 |
| b | 3 | NULL | 2 | 6 |
| b2 | 3 | NULL | NULL | 7 |
| c | 4 | NULL | NULL | 8 |
我对获取以下内容的高效查询感兴趣 table:
|field1 | field2| field3| field4|
| aaa | 1 | NULL | 1 |
| a | 2 | 0 | 1 |
| b | 3 | NULL | 2 |
| c | 4 | NULL | NULL |
基本上遵循以下规则:
- 对于 field2 的每个值,应该只有一行存在
- 在所有field2值相同的行中select依次满足以下条件的行:
- select field4 不为 Null 的行(如果可能)
- 在字段 4 具有非空值的行中 select 字段 3 具有非空值的行
- 在字段 4 和 3 具有非 Null 值的行中,select 字段 1 具有最长字符串值的行
- 满足以上条件的,select只有一行(与field5的值无关)
我可以用一堆连接来完成,但它变得非常慢。有更好的建议吗?
编辑
field2 值可能没有特定顺序。我只是把 1,2,3,4 放在例子中,但在我的例子中这通常不是真的。我没有直接在 table 上更改它,因为其中一个建议的解决方案实际上正在考虑 field2 的顺序值,所以我保留了 if 以供将来可能对此感兴趣的读者使用。
这种类型的优先排序具有挑战性。我认为 MySQL 中最简单的方法使用变量:
select t.*
from (select t.*,
(@rn := if(@f2 = field2, @rn + 1,
if(@f2 := field2, 1, 1)
)
) as seqnum
from t cross join
(select @rn := 0, @field2 := '') params
order by field2,
(field4 is not null) desc,
(field3 is not null) desc,
length(field1) desc
) t
where seqnum = 1;
我不是 100% 确定我的条件是否正确(第三个似乎与前两个冲突)。但无论优先级如何,想法都是相同的:使用 order by
以正确的顺序获取行并使用变量获取第一个。
编辑:
在 SQL 服务器——或任何其他合理的数据库——你用 row_number()
:
select t.*
from (select t.*,
row_number() over (partition by field2
order by (case when field4 is not null then 0 else 1 end),
(case when field3 is not null then 0 else 1 end),
len(field1)
) as seqnum
from t
) t
where seqnum = 1;
我有一个很大的 table,其中有大量重复的行(在我关心的那些列中)。让我从以下示例开始:
|field1 | field2| field3| field4| field5|
| aa | 1 | NULL | 1 | 0 |
| aaa | 1 | NULL | 1 | 1 |
| aaa | 1 | NULL | 1 | 2 |
| a | 2 | 0 | 1 | 3 |
| a | 2 | 0 | NULL | 4 |
| a | 2 | NULL | 2 | 5 |
| b | 3 | NULL | 2 | 6 |
| b2 | 3 | NULL | NULL | 7 |
| c | 4 | NULL | NULL | 8 |
我对获取以下内容的高效查询感兴趣 table:
|field1 | field2| field3| field4|
| aaa | 1 | NULL | 1 |
| a | 2 | 0 | 1 |
| b | 3 | NULL | 2 |
| c | 4 | NULL | NULL |
基本上遵循以下规则:
- 对于 field2 的每个值,应该只有一行存在
- 在所有field2值相同的行中select依次满足以下条件的行:
- select field4 不为 Null 的行(如果可能)
- 在字段 4 具有非空值的行中 select 字段 3 具有非空值的行
- 在字段 4 和 3 具有非 Null 值的行中,select 字段 1 具有最长字符串值的行
- 满足以上条件的,select只有一行(与field5的值无关)
我可以用一堆连接来完成,但它变得非常慢。有更好的建议吗?
编辑 field2 值可能没有特定顺序。我只是把 1,2,3,4 放在例子中,但在我的例子中这通常不是真的。我没有直接在 table 上更改它,因为其中一个建议的解决方案实际上正在考虑 field2 的顺序值,所以我保留了 if 以供将来可能对此感兴趣的读者使用。
这种类型的优先排序具有挑战性。我认为 MySQL 中最简单的方法使用变量:
select t.*
from (select t.*,
(@rn := if(@f2 = field2, @rn + 1,
if(@f2 := field2, 1, 1)
)
) as seqnum
from t cross join
(select @rn := 0, @field2 := '') params
order by field2,
(field4 is not null) desc,
(field3 is not null) desc,
length(field1) desc
) t
where seqnum = 1;
我不是 100% 确定我的条件是否正确(第三个似乎与前两个冲突)。但无论优先级如何,想法都是相同的:使用 order by
以正确的顺序获取行并使用变量获取第一个。
编辑:
在 SQL 服务器——或任何其他合理的数据库——你用 row_number()
:
select t.*
from (select t.*,
row_number() over (partition by field2
order by (case when field4 is not null then 0 else 1 end),
(case when field3 is not null then 0 else 1 end),
len(field1)
) as seqnum
from t
) t
where seqnum = 1;