mysql - 在子查询中加入 `greatest-n-per-group` 和 SUM

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

greatest-n-per-group 有超过 1800 个带标签的问题和一些出色的答案,我想我会找到这个问题的解决方案 - 但我要么错过了解决方案,要么我需要一种新方法。

我有一个表 photo_types 来存储 user 的投票,他们正在投票(赞成或反对)他们认为给定的特定 photo_type照片是。照片类型为 1-10,每次投票将为 1-1

+----+-----+-----------+------------+------+
| id | user | photo_id | photo_type | vote |
+----+------+----------+------------+------+
|  1 | jane |   photo1 |          1 |    1 |
|  2 | jane |   photo2 |          2 |    1 |
|  3 | jane |   photo3 |          4 |   -1 |
|  4 |  ben |   photo1 |          1 |    1 |
|  5 |  ben |   photo2 |          3 |   -1 |
|  6 |  ben |   photo2 |          2 |    1 |
|  7 | mary |   photo1 |          1 |   -1 |
|  8 | mary |   photo3 |         10 |    1 |
|  9 | mary |   photo2 |          1 |    1 |
| 10 | mary |   photo1 |          2 |   -1 |
+----+------+----------+------------+------+

我需要将此表左连接回 photos 表(其中包含给定照片的所有其他详细信息)- 但仅包括前 2 个投票类型 每张照片。

我需要LEFT JOIN photo_types 表的photos 表如下所示:

+----+----------+------------+----------------+---------------+------------+
| id | photo_id | photo_name |   photographer |      location |       date |
+----+----------+------------+----------------+---------------+------------+
|  1 |   photo1 | the bridge |    Bill Murray |  Brooklyn, NY | 2012-10-11 |
|  2 |   photo2 |    the cat | Jacques Chirac | Paris, France | 2013-01-03 |
|  3 |   photo3 |      a car |     the Grinch |    London, UK | 2016-09-01 |
+----+----------+------------+----------------+---------------+------------+

我显然是通过 photo_id 连接两个表。

为了获得每张照片的最高投票类型,我尝试了这样的子查询:

SELECT photo_id, photo_type, sum(vote) AS votes
FROM photo_types
GROUP BY photo_type, photo_id
HAVING votes>0
ORDER BY votes DESC

photo_typephoto_id 对投票总和进行分组。
这工作正常,但包括所有具有 sum(vote) > 0 的类型 - 而不仅仅是前 2 个投票类型。
SQL Fiddle here

当包含在联接中时,它看起来像:

SELECT * 
FROM photos
LEFT JOIN
    (SELECT photo_id, photo_type, sum(vote) AS votes
    FROM photo_types
    GROUP BY photo_type, photo_id
    HAVING votes>0
    ORDER BY votes DESC) AS pt
ON photos.photo_id = pt.photo_id
WHERE photos.date > '2010-01-01';

SQL Fiddle here

我曾希望使用 Bill Karwin's solution但我无法根据分组值(在我的例子中是 SUM)将表连接到自身。我试过的子查询看起来像:

SELECT pt1.*, SUM(pt1.vote) AS votes1, SUM(pt2.vote) AS votes2
FROM photo_types AS pt1
LEFT OUTER JOIN photo_types AS pt2
    ON pt1.photo_id = pt2.photo_id
        AND (votes1 < votes2
        OR (votes1 = votes2 AND pt1.id < pt2.id))
WHERE pt2.photo_id IS NULL

...这不起作用,因为它试图根据计算值连接两个表(与 Bill 的解决方案不同)。
SQL Fiddle here

问题
当分组基于诸如 SUM(xxx) 之类的计算值时,是否有办法获得 greatest-n-per-group

部分解决这个问题的解决方案是 herehere但不要在分组值中包含聚合。

另一种明显的方法是在每次投票时重新计算最高投票值,并将其直接存储在 photos 表中 - as discussed here - 但除非不可能 - 出于各种原因,我更愿意在 SELECT 中进行计算。

最佳答案

如果列表有限,最简单的方法是 substring_index()/group_concat() 技巧:

SELECT photo_id,
       SUBSTRING_INDEX(GROUP_CONCAT(photo_type ORDER BY votes DESC), ',', 2) as top2
FROM (SELECT photo_id, photo_type, sum(vote) AS votes
      FROM photo_types
      GROUP BY photo_type, photo_id
      HAVING votes > 0
     ) pt
GROUP BY photo_id;

注意事项:

  • group_concat() 的中间字符串大约为 1k —— 这对于这个问题来说绰绰有余。
  • 备选方案(如您所见)要么使用变量进行更复杂的查询。

关于mysql - 在子查询中加入 `greatest-n-per-group` 和 SUM,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41475437/

相关文章:

在 sequelize 中连接表

mysql - 如何编写这个复杂的自连接

mysql - 如何检查记录是否存在,如果不存在则插入

mysql - WP 如何猜测新帖子使用哪个 ID?

mysql - 如何提高具有不依赖子查询的 MySQL 查询的性能?

Android SQLite 数据库未填充

php - 合并来自两个不同 MySql 查询的两个不同数组

java - 如何配置Tomcat连接MySQL

sql - PostgreSQL:从 5 分钟前的表中获取行

mysql - 如何计算sql查询中总数的百分比