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