Mysql 是否可以在聚合查询中向MAX()调用添加条件? 背景
我的典型用例:Mysql 是否可以在聚合查询中向MAX()调用添加条件? 背景,mysql,group-by,greatest-n-per-group,Mysql,Group By,Greatest N Per Group,我的典型用例: # Table id category dataUID --------------------------- 0 A (NULL) 1 B (NULL) 2 C text1 3 C text1 4 D text2 5 D text3 # Query SELECT MAX(`id`)
# Table
id category dataUID
---------------------------
0 A (NULL)
1 B (NULL)
2 C text1
3 C text1
4 D text2
5 D text3
# Query
SELECT MAX(`id`) AS `id` FROM `table`
GROUP BY `category`
这很好;它将删除正在处理的记录集中的任何“重复类别”,为每个类别提供“最高”ID
然后,我可以继续使用此ID再次提取所有数据:
# Query
SELECT * FROM `table` JOIN (
SELECT MAX(`id`) AS `id` FROM `table`
GROUP BY `category`
) _ USING(`id`)
# Result
id category dataUID
---------------------------
0 A (NULL)
1 B (NULL)
3 C text1
5 D text3
请注意,这与:
SELECT MAX(`id`) AS `id`, `category`, `dataUID` FROM `table`
GROUP BY `category`
Per:
在标准SQL中,包含GROUPBY子句的查询不能引用
选择列表中未在列表中命名的未聚合列
按子句分组。例如,此查询在标准SQL中是非法的
因为“选择”列表中的“名称”列不显示在
分组人:
SELECT o.custid, c.name, MAX(o.payment) FROM orders AS o, customers
AS c WHERE o.custid = c.custid GROUP BY o.custid;
要使查询合法,必须从
在GROUPBY子句中选择list或NAME
MySQL扩展了GROUPBY的使用,以便选择列表可以引用
GROUP BY子句中未命名的非聚合列。这意味着
前面的查询在MySQL中是合法的。您可以使用此功能
通过避免不必要的列排序和
分组但是,这主要是在每个
未在GROUP BY中命名的未聚合列对于每个列都是相同的
组。
[……]
此扩展假定未分组的列具有相同的分组值。否则,结果是不确定的。
因此,我将为dataUID
获取一个未指定的值——例如,对于id为id的结果,text2
或text3
在我的真实案例中,这实际上是其他领域的一个问题;碰巧的是,对于dataUID
列,通常我并不在乎得到哪个值
问题
然而
如果给定类别
的任何行具有NULL
dataUID
,并且至少有一行具有非NULL
dataUID
,我希望MAX
忽略NULL
行
因此:
目前,由于我选择了ID最大的行,我得到:
5 D (NULL)
但是,因为dataUID
是NULL
,所以我想要:
4 D text2
我怎样才能得到这个?如何将条件逻辑添加到聚合MAX
?
我想可能会交给MAX
一个元组,然后从中取出id
:
GET_SECOND_PART_SOMEHOW(MAX((IF(`dataUID` NOT NULL, 1, 0), `id`))) AS `id`
但我不认为MAX会接受这样的任意表达式,更不用说元组了,而且我不知道在事实发生后如何检索元组的第二部分。根据我记忆中的情况,可以在分组语句中使用。比如说
SELECT MAX(COALESCE(`id`,1)) ...
嗯,看来我第一次读得很快。我想也许你想要这样的东西
SELECT * FROM `table` JOIN (
SELECT MAX(`id`) AS `id` FROM `table`
WHERE `dataUID` IS NOT NULL
GROUP BY `category`
) _ USING(`id`)
或许
SELECT MAX(`id`) AS `id`,
COALESCE (`dataUID`, 0) as `dataUID`
FROM `table`
GROUP BY `category`
稍微调整一下。要获取id
s,您可以使用
SELECT COALESCE(MAX(CASE
WHEN dataUID IS NOT NULL THEN id
END), MAX(id)) AS id
FROM table
GROUP BY category
然后把它插入到一个连接中
这比我想象的要容易,最终,因为MySQL将接受MAX
中的任意表达式
select *
from t1
join (
select max(id) as id,
max(if(dataGUID is NULL, NULL, id)) as fallbackid,
category
from t1 group by category) as ids
on if(ids.id = fallbackid or fallbackid is null, id, fallbackid) = t1.id;
我可以通过在id
中插入一个主角作为排序提示来获得所需的排序:
SUBSTRING(MAX(IF (`dataUID` IS NULL, CONCAT('a',`id`), CONCAT('b',`id`))) FROM 2)
走查:
id category dataUID IF (`dataUID` IS NULL, CONCAT('a',`id`), CONCAT('b',`id`)
--------------------------------------------------------------------------------------
0 A (NULL) a0
1 B (NULL) a1
2 C text1 b2
3 C text1 b3
4 D text2 b4
5 D (NULL) a5
因此:
那么,再把订购暗示删掉就很简单了
特别感谢@JlStone通过COALESCE
将我设置在MAX
调用中嵌入表达式的路径上,并直接操作提供给MAX
的值
select *
from t1
join (
select max(id) as id,
max(if(dataGUID is NULL, NULL, id)) as fallbackid,
category
from t1 group by category) as ids
on if(ids.id = fallbackid or fallbackid is null, id, fallbackid) = t1.id;
SELECT id, category,dataUID
FROM
(
SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY id desc, dataUID desc ) rn,
id, category,dataUID FROM table
) q
WHERE rn=1
考虑到按desc排序最终会移动空值。OK,所以我会编写COALESCE(`dataUID`,`id`)
,但接下来我会为一些行获取dataUID
,为其他行获取id
。。将MAX
应用于此将产生令人惊讶的结果。MAX(COALESCE(如果(`dataUID`不为NULL,CONCAT('b','id`),NULL),CONCAT('a','id`))
可能会满足我的要求。我总是在结果中得到id
,但我也得到了非dataUID
之前的“dataUID
”排序信息;我以后可以去掉那个角色。。。嗯…@Tomalak你的意思是像MAX(如果(`dataUID`为NULL,-1,`id`))那样的'id`
?@JlStone:朝那个方向走。我想我现在明白了。谢谢你的提示!不,因为那样的话,我就不会得到类别A
或B
的任何结果了!来自q:“如果给定类别的任何一行具有空dataUID,并且至少有一行具有非空dataUID,我希望MAX忽略这些空dataUID。”如果没有一行具有非空dataUID,我希望返回到ID排序。是的,我想这样就可以了。Martin's需要对我现有的查询进行更本地化的修改,我发现它更容易理解,所以这就是我要做的。但还是要谢谢你!如果一个类别的所有行都有dataUID NULL
,会发生什么情况?@ypercube:退回到按ID排序。我只想对任何dataUID
-在进行排序时,显示组中确实存在的行进行优先级排序。@ajreal:Ta。我找不到明显不合适的max
tag;)的替代品@ajreal:是的,我想:)这似乎产生了与我的解决方案相同的结果,尽管我一辈子都不知道如何:DNice。简单而且可能比我的最终版本快得多。我相信这也能起到作用。谢谢是ids.id=fallbackid还是
冗余?@Tomalak,不是,当dataGUID只有max id为null时,它是必需的。但是如果ids.id=fallbackid
那么两个操作数中的任何一个都可以使用,因为根据条件发现,它们是等效的?@Tomalak,fallbackid为null的条件只有在所有DataGuid为null时才为真,应该有一个附加条件OVER
来自Oracle,不是吗?我不知道MySQL支持它
select *
from t1
join (
select max(id) as id,
max(if(dataGUID is NULL, NULL, id)) as fallbackid,
category
from t1 group by category) as ids
on if(ids.id = fallbackid or fallbackid is null, id, fallbackid) = t1.id;
SELECT id, category,dataUID
FROM
(
SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY id desc, dataUID desc ) rn,
id, category,dataUID FROM table
) q
WHERE rn=1