两个联接的聚合(MySQL)

时间:2009-02-18 20:09:09

标签: sql join aggregation

我有一个名为图库的表格。对于图库中的每一行,表格图片中有多行。一张图片属于一个图库。然后是表格投票。每一行都是某个画廊的upvote或downvote。 这是(简化)结构:

gallery ( gallery_id )
picture ( picture_id, picture_gallery_ref )
vote ( vote_id, vote_value, vote_gallery_ref )

现在我想要一个查询给我以下信息:所有图库都有自己的数据字段,连接到图库的图片数量和投票的价值。

这是我的查询,但由于多次加入,聚合值不正确。 (至少当图片或投票有多行时。)

SELECT 
  *, SUM( vote_value ) as score, COUNT( picture_id ) AS pictures
FROM 
  gallery
LEFT JOIN 
  vote
  ON gallery_id = vote_gallery_ref
LEFT JOIN 
  picture
  ON gallery_id = picture_gallery_ref
GROUP BY gallery_id

因为我注意到COUNT( DISTINCT picture_id )给了我正确数量的照片,我试过这个:

( SUM( vote_value ) / GREATEST( COUNT( DISTINCT picture_id ), 1 ) ) AS score

它适用于此示例,但如果一个查询中有更多连接,该怎么办?

只想知道是否有更好或更“优雅”的方式可以解决这个问题。另外我想知道我的解决方案是MySQL特定的还是标准的SQL?

4 个答案:

答案 0 :(得分:5)

来自Okham的William的引用适用于此:

  

Enita non sunt multiplicanda praeter necessitatem

     

(拉丁语称“实体不得超越必要性”)。

您应该重新考虑为什么需要在单个查询中完成此操作?单个查询的开销确实比多个查询要少,但如果单个查询的性质变得过于复杂,无论是为了开发还是为了执行RDBMS,然后运行单独的查询。

答案 1 :(得分:3)

或者只使用子查询...

我不知道这是否是有效的MySQL语法,但您可以做类似的事情:

SELECT
  gallery.*, a.score, b.pictures
LEFT JOIN
(
  select vote_gallery_ref, sum(vote_value) as score
  from vote
  group by vote_gallery_ref
) a ON gallery_id = vote_gallery_ref
LEFT JOIN 
(
  select picture_gallery_ref, count(picture_id) as pictures
  from picture
  group by picture_gallery_ref
) b ON gallery_id = picture_gallery_ref

答案 2 :(得分:1)

您多久添加/更改一次投票记录?

您多久添加/删除图片记录?

您经常针对这些总计运行此查询吗?

最好在图库表(total_pictures, total_votes, total_vote_values)上创建总字段。

在图片表上添加或删除记录时,还会更新图库表中的总计。这可以使用图片表上的触发器来自动更新库表。它也可以使用组合两个SQL语句的事务来更新图片表和库表。在图片表上添加记录时,会增加图库表上的total_pictures字段。删除图片表上的记录时,会减少total_pictures字段。

类似地,当添加或删除投票记录或vote_value更改时,您更新了total_votestotal_vote_values字段。添加记录会增加total_votes字段,并将vote_values添加到total_vote_values。删除记录会减少total_votes字段,并从vote_values中减去total_vote_values。更新投票记录中的vote_values也应使用差异更新total_vote_values(减去旧值,添加新值)。

您的查询现在变得微不足道 - 它只是来自图库表的简单查询。但这是以更复杂的图片和投票表更新为代价的。

答案 3 :(得分:0)

正如Bill Karwin所说,在一个查询中执行此操作非常难看。

但是,如果你必须这样做,加入并选择带有聚合数据的非聚合数据需要加入子查询(我在过去几年里没有使用过那么多的SQL,所以我实际上忘记了这个的正确用语)

我们假设您的图库表包含其他字段namestate

select g.gallery_id, g.name, g.state, i.num_pictures, j.sum_vote_values
from gallery g
inner join (
  select g.gallery_id, count(p.picture_id) as 'num_pictures'
  from gallery g
  left join picture p on g.gallery_id = p.picture_gallery_ref
  group by g.gallery_id) as i on g.gallery_id = i.gallery_id
left join (
  select g.gallery_id, sum(v.vote_value) as 'sum_vote_values'
  from gallery g
  left join vote v on g.gallery_id = v.vote_gallery_ref
  group by g.gallery_id
) as j on g.gallery_id = j.gallery_id

这将产生一个如下所示的结果集:

gallery_id, name, state, num_pictures, sum_vote_values
1, 'Gallery A', 'NJ', 4, 19
2, 'Gallery B', 'NY', 3, 32
3, 'Empty gallery', 'CT', 0,