MySQL:ORDER BY + GROUP BY和自定义订单

时间:2012-02-23 17:13:21

标签: mysql group-by sql-order-by

我有一个与MySQL有关的问题。这是我的表格:

keyword   args     title    namespace_id      ratio
en        1        A        23                0.5
en        1        B        89                0.6
en        0        C        89                0.4
foo       1        Foo      23                0.7
bar       1        Bar      89                0.3

我想要一个没有(keywords,args)重复的所有行的列表。如果存在重复项,则应按我提供的namespace_ids顺序选择所选行。左边的行应按比例排序。

使用namespace_id命令23,89,x,y:

的示例结果
keyword   args     title    namespace_id      ratio
foo       1        Foo      23                0.7
en        1        A        23                0.5
en        0        C        89                0.4
bar       1        Bar      89                0.3

命名空间顺序为89,23,x,y的示例结果:

keyword   args     title    namespace_id      ratio
foo       1        Foo      23                0.7
en        1        B        89                0.6
en        0        C        89                0.4
bar       1        Bar      89                0.3

我有没有办法直接在MySQL中使用它?我查看了GROUP BY,ORDER BY并注意到了GROUP_CONCAT()函数,但我没有设法正确地将它们放在一起。我想要的声明是:

  SELECT keyword, args, title, namespace_id, ratio 
    FROM tbl 
GROUP BY keyword, args 
ORDER BY ratio DESC;

但现在我不知道如何引入namespace_id命令。

我发现了类似的问题: MySQL: "order by" inside of "group by" 答案那里接近我想要的,但是,我的namespace_id顺序是变化的,不能通过MAX()函数计算。

编辑:这里的挑战是告诉GROUP BY选择哪一行。正常的ORDER BY显然没有,它只使用GROUP BY的输出。

5 个答案:

答案 0 :(得分:1)

尝试使用FIELD()和子查询:

SELECT t1.keyword, t1.args, t1.title, t1.namespace_id, t1.ratio
FROM tbl t1, (SELECT keyword, args, MIN(FIELD(namespace_id, 23, 89))
minfield FROM tbl GROUP BY keyword, args) t2
WHERE t1.keyword = t2.keyword AND t1.args = t2.args AND
FIELD(t1.namespace_id, 23, 89) = t2.minfield
ORDER BY ratio DESC;

或自我加入:

SELECT t1.keyword, t1.args, t1.title, t1.namespace_id, t1.ratio
FROM tbl t1
LEFT OUTER JOIN tbl t2 ON
    t1.keyword = t2.keyword AND
    t1.args = t2.args AND
    FIELD(t1.namespace_id, 23, 89) < FIELD(t2.namespace_id, 23, 89)
WHERE t2.keyword IS NULL AND t2.args IS NULL
ORDER BY t1.ratio DESC;

编辑:看一些支持OLAP操作的(商业)DBMS也是值得的(如果你能够选择,如果你正在处理大量数据)。对于Georg的案例,我认为OVER和PARTITION BY关键字会有所帮助:http://publib.boulder.ibm.com/infocenter/rbhelp/v6r3/topic/com.ibm.redbrick.doc6.3/sqlrg/sqlrg36.htm#sii06377181

答案 1 :(得分:0)

我认为这就是你想要的 -

SELECT t1.* FROM tbl t1
  JOIN(SELECT keyword, args, MIN(ratio) ratio FROM tbl GROUP BY keyword, args) t2
    ON t1.keyword = t2.keyword AND t1.args = t2.args AND t1.ratio = t2.ratio
  ORDER BY ratio DESC;

和第二个 -

SELECT t1.* FROM tbl t1
  JOIN(SELECT keyword, args, MAX(ratio) ratio FROM tbl GROUP BY keyword, args) t2
    ON t1.keyword = t2.keyword AND t1.args = t2.args AND t1.ratio = t2.ratio
  ORDER BY ratio DESC;

答案 2 :(得分:0)

您可以使用FIELD()生成如下自定义排序顺序:

SELECT keyword, args, title, namespace_id, FIELD(namespace_id, 32, 89) sorting, ratio 
FROM tbl 
GROUP BY keyword, args
ORDER BY sorting DESC, ratio DESC;

请注意,FIELD()函数中未指定的任何namespace_id都将收到0 sorting值,因此要在结果中显示 first 这些项目,您必须指定它们的顺序相反,并使用DESC作为排序顺序。

因此FIELD(namespace_id, 32, 89) ORDER BY sorting DESC会导致:

x x 89 x
x x 32 x
...

FIELD(namespace_id, 89, 32)ORDER BY sorting DESC的结果将导致:

x x 32 x
x x 89 x
...

答案 3 :(得分:0)

执行此操作的一种略微讨厌的方法是按照许多布尔表达式排序,如下所示:

SELECT keyword, args, title, namespace_id, ratio 
FROM tbl 
GROUP BY keyword, args 
ORDER BY namespace_id != 89,
         namespace_id != 23,
         namespace_id != x,
         namespace_id != y,
         ratio DESC;

显然,这很快变得不切实际。

如果您真的必须在SQL端执行此操作,我建议您创建另一个包含列namespace_id(应该具有UNIQUE约束)和priority(或类似)的表。然后,您在该表中JOINORDER BY priority

您可能已经拥有namespace_id引用的命名空间的表。在这种情况下,只需在该表中添加priority列。

答案 4 :(得分:0)

重新考虑您的问题以及您对我之前回答的评论后,我认为您无法做到这一点。原因如下:

由于您希望在排序之后过滤行,因此您唯一的选择是HAVING子句,据我所知,这是ORDER BY之后处理的唯一事物。条款。

由于HAVING子句仅单独查看每一行,但您希望按其在集合中的相对位置进行过滤(即您只希望每个子集的第一行具有相同的关键字/ arg),必须将其作为一个值“走私”到每一行。我简单地想过这个选项,但只能提出废话。

跳过GROUP,获取ORDER权限(通过将可排序的priority值与每个namespace_id相关联),然后在您的代码中,使用键入keywordarg的数据结构,并在使用结果集填充时忽略重复项。

如果出于某种神秘的原因绝对必须在SQL中执行所有操作,您可以通过创建在两列上具有UNIQUE约束的临时表(在内存中)来模拟我上面描述的内容并执行{{ 1}}


作为旁注:每当您注意到您希望SQL对依赖于结果集中其他行的行执行某些操作时,您可能会运气不好。我反复讨论过我认为很容易的案例,直到我发现我接近结果集,好像它们是循环。我最喜欢的轶事是我拼命地从前一行中的对应物中减去一列中的值的时间。有时候你可以试着用怪异的INSERT IGNORE INTO temp_table SELECT ...来破解你的出路(将相关的行拉到一起),但即使这样可行,也可能很贵。