【问题标题】:how to return more than one row per GROUP BY condition每个 GROUP BY 条件如何返回多于一行
【发布时间】:2013-11-22 03:33:49
【问题描述】:

我正在尝试获取给定 GROUP BY 条件的 TOP X 结果。 我目前正在使用这样的东西:

SELECT * FROM 
        (SELECT id  
            FROM myTable 
            WHERE id IN (x1, x2, ..., xn) GROUP BY id ORDER BY grade DESC 
        ) t1 
        INNER JOIN myTable t2 ON t2.id=t1.id

id 是一个非唯一的 INT 索引字段,每个值有多个行。

这将为每个 id 返回我,即成绩最好的行。如何转换它以返回每个 id 的 TOP X 结果?

例如,对于以下数据

id  grade
2   10
2   13
2   15
3   20
4   16
4   55
4   45
4   35
4   25
5   1

假设 TOP X 中的 X 为 2,我想获取以下行:

id grade  
2  15  
2  13  
3  20  
4  50  
4  40  
5  1  

【问题讨论】:

  • Top x 结果来自哪个表?
  • 加了个例子,更清楚了吗?
  • 如果有15 13 13id = 2 的结果怎么办?那你会显示 3 吗?
  • 如果出现平局怎么办?例如,给定 id 的 3 条记录具有相同的等级?
  • @Przemyslaw Kruglej:如果 SO 成员在同一时刻想法相同怎么办? ;-)

标签: mysql sql group-by query-optimization greatest-n-per-group


【解决方案1】:

这取决于您是否希望将领带退回。

如果您想返回领带,您可以使用以下方法:

CREATE TABLE grades (
  id INT,
  grade INT
);

INSERT INTO grades VALUES (1, 2);
INSERT INTO grades VALUES (1, 3);
INSERT INTO grades VALUES (1, 4);
INSERT INTO grades VALUES (1, 5);

INSERT INTO grades VALUES (2, 5);
INSERT INTO grades VALUES (2, 5);
INSERT INTO grades VALUES (2, 5);
INSERT INTO grades VALUES (2, 4);

INSERT INTO grades VALUES (3, 3);
INSERT INTO grades VALUES (3, 4);

INSERT INTO grades VALUES (4, 3);

SELECT id, grade
  FROM grades g
WHERE (
   SELECT COUNT(DISTINCT grade) FROM grades
   WHERE id = g.id
      AND grade >= g.grade
) <= 2;

输出:

身份证等级
1 4
1 5
2 5
2 5
2 5
2 4
3 3
3 4
4 3

如果您不想要领带,请使用 DISTINCT:

SELECT DISTINCT id, grade
  FROM grades g
WHERE (
   SELECT COUNT(DISTINCT grade) FROM grades
   WHERE id = g.id
      AND grade >= g.grade
) <= 2;

输出:

身份证等级
1 4
1 5
2 5
2 4
3 3
3 4
4 3

SQLFiddle:SQLFiddle

【讨论】:

  • 有什么办法可以避免子查询?需要检查,但假设这将成为性能杀手。这旨在在具有数十亿行的表上运行。
  • @Noam:为什么不将操作数据存储在单独的表中(分片、分区等)?
  • @Noam 你本可以在你的问题中提到它。 google上有很多关于这个话题的,例如:TOP X In Mysql #1TOP X In Mysql #2
【解决方案2】:

也许,像这样?

SELECT m.*
FROM (
  SELECT id
  FROM myTable 
  WHERE id IN (1, 3)
  GROUP BY id
) AS ids
RIGHT JOIN myTable AS m ON ids.id = m.id
WHERE
  m.id = ids.id AND
  m.grade IN (
    SELECT TOP 5 t.grade
    FROM myTable AS t
    WHERE t.id = ids.id
    ORDER BY t.grade DESC
  );

更新: 或者,甚至

SELECT m.*
FROM myTable AS m
WHERE
  m.id IN (1, 2) AND
  m.grade IN (
    SELECT TOP 5 t.grade
    FROM myTable AS t
    WHERE t.id = m.id
    ORDER BY t.grade DESC
  );

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-30
    • 1970-01-01
    • 1970-01-01
    • 2020-12-20
    • 1970-01-01
    • 2014-09-06
    • 2017-10-29
    • 1970-01-01
    相关资源
    最近更新 更多