我有一个名为图库的表格。对于图库中的每一行,表格图片中有多行。一张图片属于一个图库。然后是表格投票。每一行都是某个画廊的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?
答案 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_votes
和total_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,所以我实际上忘记了这个的正确用语)
我们假设您的图库表包含其他字段name
和state
:
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,