SQL仅选择列上具有自定义最大值的行

时间:2013-12-10 10:26:36

标签: mysql sql greatest-n-per-group

我想要一些关于MySQL请求的帮助,以便只选择列上具有最大值的行。

我已经阅读并实施了那里给出的解决方案: SQL Select only rows with Max Value on a Column

SELECT * FROM yourtable AS yt1
LEFT OUTER JOIN yourtable yt2 ON (yt1.id = yt2.id AND yt1.rev < yt2.rev)
WHERE yt2.id IS NULL;

并且它工作正常,但只要您要过滤的字段实际上是最大值或最小值。

如果您想过滤自定义订单,该怎么办?

id   rev   content..
.1.  1 
.2   1......---...
.1   2......---...
.1   3......---...

也就是说,您不希望在上表中选择两行:[1, 3, ...][2, 1, ..],而是希望您的结果集为[1, 2, ...][2, 1, ..],因为它一直是确定rev = 2的优先级高于rev=3rev=1

您是否需要在“&lt;”或其他内容上重新定义订单关系?

我宁愿避免使用子选择。

感谢。

2 个答案:

答案 0 :(得分:0)

示例数据:

CREATE TABLE yourtable
    (`id` int, `rev` int, content varchar(20))
;

INSERT INTO yourtable
    (`id`, `rev`, content)
VALUES
    (1, 1, 'a'),
    (2, 1, 'b'),
    (1, 2, 'c'),
    (1, 3, 'd')
;

解决方案1:

SELECT * FROM yourtable AS yt1
LEFT OUTER JOIN yourtable yt2 ON (yt1.id = yt2.id AND FIELD(yt1.rev, 1,3,2) < FIELD(yt2.rev, 1,3,2))
WHERE yt2.id IS NULL;

但我不喜欢在连接上的列上使用函数,尤其是在进行范围比较而不是相等比较时。

解决方案2:

SELECT id, rev, content FROM (
  SELECT yt.*, @grpnum := IF(id != @prevgrp, 1, @grpnum + 1) as my_filter, @prevgrp := id 
  FROM yourtable yt, 
  (SELECT @grpnum:=0, @prevgrp := 0) v 
  ORDER BY id, FIELD(rev, 1, 3, 2) DESC
) sq
WHERE my_filter = 1;

<强>结果:

| ID | REV | CONTENT |
|----|-----|---------|
|  1 |   2 |       c |
|  2 |   1 |       b |

解释解决方案2:

首先,我从yourtable中选择所有内容,然后按id排序,然后按FIELD(rev, 1, 3, 2)排序。这个FIELD()函数有什么作用?

考虑这些数据:

mysql> select id from value order by id;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
|  6 |
+----+

现在,当你做

mysql> select id, field(id, 3,2,5) from value order by id;
+----+------------------+
| id | field(id, 3,2,5) |
+----+------------------+
|  1 |                0 |
|  2 |                2 |
|  3 |                1 |
|  4 |                0 |
|  5 |                3 |
|  6 |                0 |
+----+------------------+

您会看到该函数返回函数参数列表中列id的索引。找不到的所有内容都将返回0

详细了解here in the manual

由于我们希望获得最大值,因此我们在FIELD()的参数列表中翻转顺序。我们假设FIELD(rev, 2, 3, 1) 不是FIELD(rev, 1, 3, 2),而是优先顺序为2,最高为3,然后为1 (编辑:我刚刚看到,你有另一个订单,你当然也可以使用你的CASE WHEN构造,它也是这样,FIELD()函数只是写的少了)< / em>的

下一步是在使用v别名的子查询中动态初始化一些变量。这与在查询之前编写SET @grpnum = 0;相同。

当ID发生变化时(请注意,我们按id排序很重要,然后按field()),我们会增加另一个变量。这在外部查询中用作过滤器。

这样我们就会返回FIELD()函数中与您定义的最大值对应的所有行。

答案 1 :(得分:0)

也许是这样的,在子查询中指定优先级(你可以建立)

SELECT * 
FROM yourtable AS yt1
LEFT OUTER JOIN (SELECT 1 AS rev, 9 AS rev_priority UNION SELECT 2, 9 UNION SELECT 3, 9) yt1_priority ON yt1.rev = yt1_priority.rev
LEFT OUTER JOIN yourtable yt2 
ON yt1.id = yt2.id 
LEFT OUTER JOIN (SELECT 1 AS rev, 9 AS rev_priority UNION SELECT 2, 9 UNION SELECT 3, 9) yt1_priority ON yt1.rev = yt2_priority.rev
ON yt1_priority.rev_priority < yt2_priority.rev_priority
WHERE yt2.id IS NULL;

(未经测试)