简体   繁体   English

SQL:按组汇总查询结果

[英]SQL: Sum the results of a query by group

I just started learning SQL a few days ago and I am working with some basic examples.几天前我刚刚开始学习 SQL,我正在研究一些基本的例子。 In this example I want to find trainings that my friends and teammates like (based on how many of my friends liked the training, how many friends completed the training, and the average rating from my friends on the training).在这个例子中,我想找到我的朋友和队友喜欢的训练(基于我有多少朋友喜欢训练,有多少朋友完成了训练,以及我的朋友对训练的平均评分)。 So far I used sub-queries to get me a weighted value for each type of weighted value per training.到目前为止,我使用子查询为每次训练的每种类型的加权值获取加权值。 I want to sum the weighted values for each training giving each training a total and then order the training list by the total and select the top few from that.我想对每个训练的加权值求和,给每个训练一个总数,然后按总数对训练列表进行排序,并从中选择前几个。

Here is what I did.这是我所做的。

SELECT training_id, (5* COUNT()) AS [value]FROM progress
WHERE user_id IN (SELECT friend_id AS user_ids FROM friendships WHERE user_id=6 UNION SELECT user_id FROM membership WHERE team_id IN (SELECT team_id FROM membership WHERE user_id = 6))
AND completed = 1
GROUP BY training_id

UNION ALL

SELECT training_id, (10 * AVG(rating))
FROM reviews
WHERE user_id IN (SELECT friend_id AS user_ids FROM friendships WHERE user_id=6 UNION SELECT user_id FROM membership WHERE team_id IN (SELECT team_id FROM membership WHERE user_id = 6))
GROUP BY training_id

UNION ALL

SELECT training_id, COUNT()
FROM likes
WHERE user_id IN (SELECT friend_id AS user_ids FROM friendships WHERE user_id=6 UNION SELECT user_id FROM membership WHERE team_id IN (SELECT team_id FROM membership WHERE user_id = 6))
GROUP BY training_id

The results look like:结果如下:

training_id     value
______________________
1                10
2                5
1                34.5
2                45
1                6
2                3

Please let me know if you know of a way to do this or if there is an alternative to this method that I should be looking at.如果您知道这样做的方法,或者是否有我应该考虑的替代方法,请告诉我。

(the tables 'likes' 'reviews' and 'progress' all contain the field "training_id". What do you think about Joining the three tables to begin with?) (表'likes' 'reviews' 和'progress' 都包含字段“training_id”。你怎么看待加入这三个表开始?)

This is quite impressive.这相当令人印象深刻。 Your query is well structured and easy to read.您的查询结构良好且易于阅读。 If you want sums per training_id, simply build an outer query upon your own:如果您想要每个 training_id 的总和,只需自己构建一个外部查询:

select training_id, sum(value)
from (<your query here>) x
group by training_id
order by sum(value) desc;

As to joining the tables: No, generally you don't want to do this.至于加入表格:不,通常你不想这样做。 You don't want to join each progress record with each review record and each likes record per training_id.您不想将每条进度记录与每条评论记录和每条training_id 的每条喜欢记录连接起来。 This would multiply your values (eg 2 progress entries, 3 reviews, and 4 likes for one training would give you 2 x 3 x 4 = 24 intermediate records).这将使您的值成倍增加(例如,一次培训的 2 个进度条目、3 条评论和 4 个赞将为您提供 2 x 3 x 4 = 24 条中间记录)。 In your specific case this would do no harm, as AVG would result in the same value and you'd replace COUNT(*) with COUNT(DISTINCT progress_id) and COUNT(DISTINCT likes_id) , but once you'd use SUM you're in trouble.在您的特定情况下,这不会有什么害处,因为AVG会产生相同的值,您可以将COUNT(*)替换为COUNT(DISTINCT progress_id)COUNT(DISTINCT likes_id) ,但是一旦您使用SUM您就会麻烦。

What you could do is join the aggregates:你可以做的是加入聚合:

with mates as
(
  select friend_id as user_id from friendships where user_id = 6
  union 
  select user_id from membership where team_id in 
               (select team_id from membership where user_id = 6)
)
select 
  training_id, 
  coalesce(p.value, 0) + 
  coalesce(r.value, 0) + 
  coalesce(l.value, 0) as total
from
(
  select training_id, 5 * count(*) as value
  from progress
  where user_id in (select user_id from mates)
  and completed = 1
  group by training_id
) p
full outer join
(
  select training_id, 10 * avg(rating) as value
  from reviews
  where user_id in (select user_id from mates)
  group by training_id
) r using (training_id)
full outer join
(
  select training_id, count(*) as value
  from likes
  where user_id in (select user_id from mates)
  group by training_id
) l using (training_id)
order by 2 desc;

OK, thank you for your input.好的,感谢您的输入。 I was right on my second hunch.我的第二个预感是对的。 I needed to use Joins.我需要使用连接。 I only read about Joins in the documentation and wasn't familiar with them, but once I tried it, it was pretty straight forward.我只在文档中阅读了 Joins 并且不熟悉它们,但是一旦我尝试过它,它就非常简单。

Here is the solution.这是解决方案。

SELECT training.*, ((10 * AVG(reviews.rating)) + COUNT(DISTINCT likes.user_id) + ( 5* COUNT(DISTINCT progress.trainee_id))) AS total FROM training JOIN likes ON training.training_id = likes.training_id JOIN reviews ON training.training_id = reviews.training_id JOIN progress ON training.training_id = progress.training_id WHERE user_id IN (SELECT friend_id AS user_ids FROM friendships WHERE user_id=6 UNION SELECT user_id FROM membership WHERE team_id IN (SELECT team_id FROM membership WHERE user_id = 6)) AND progress.completed = 1 AND progress.trainee_id IN (SELECT friend_id AS user_ids FROM friendships WHERE user_id=6 UNION SELECT user_id FROM membership WHERE team_id IN (SELECT team_id FROM membership WHERE user_id = 6)) ORDER BY total DESC GROUP BY training_name LIMIT 6

This gave me这给了我
training_id, training_name, total --these are the field names of the result training_id, training_name, total -- 这些是结果的字段名
1, Beginner LiveCode, 53 1、初学者LiveCode,53
2, SQL 101, 48 2、SQL 101、48

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM