【问题标题】:JOIN with `greatest-n-per-group` and SUM in sub-query在子查询中加入 `greatest-n-per-group` 和 SUM
【发布时间】:2017-01-05 00:26:33
【问题描述】:

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 内进行计算。

【问题讨论】:

  • 想要的结果在哪里?
  • @Strawberry 与 sqlfiddle.com/#!9/2029d8/7 相同,但仅包括每个 photo_id 的前 2 个投票类型。

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


【解决方案1】:

如果您的列表有限,最简单的方法是 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 —— 这对于这个问题来说绰绰有余。
  • 替代方案(如您所见)要么使用变量进行更复杂的查询。

【讨论】:

  • 谢谢-这确实有效-sqlfiddle.com/#!9/2029d8/11-但如果我需要基于photo_type 进行选择,则不适用于标准WHERE photo_type IN (array)-并且可能需要REGEXP。有什么方法可以避免结果中的逗号分隔值?
【解决方案2】:

查找 xxx 应用函数。它们为您提供了比仅进行子聚合查询更大的灵活性。

http://sqlserverplanet.com/sql-2005/cross-apply-explained

【讨论】:

【解决方案3】:

好的,从这个old blog post (在其他greatest-n-per-group 解决方案中提到几次),以下工作:

SELECT pt1.*
FROM 
  (SELECT id, photo_id, photo_type, sum(vote) AS votes
  FROM photo_types
  GROUP BY photo_type, photo_id
  HAVING votes>0) AS pt1
WHERE (
  SELECT COUNT(*) 
  FROM 
    (SELECT id, photo_id, photo_type, sum(vote) AS votes
    FROM photo_types
    GROUP BY photo_type, photo_id
    HAVING votes>0) AS pt2
  WHERE pt1.photo_id = pt2.photo_id and pt1.votes <= pt2.votes
) <=2
ORDER BY photo_id, votes DESC

see SqlFiddle here

但是:
- 不确定它的效率如何,因为它使用两个子查询
- 如果 greatest-n 中的任何一个具有相同的值(因为这会将计数推到指定限制之外),则不会返回正确数量的结果 - 如您所见 in this SqlFiddle

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-07
    • 2021-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多