带连接的Sql条件计数

时间:2016-01-07 14:21:01

标签: mysql sql

我无法在stackoverflow上找到我的问题的答案。我有一个跨越3个表的查询:

newsitem
+------+----------+----------+----------+--------+----------+
| Guid | Supplier | LastEdit | ShowDate |  Title | Contents |
+------+----------+----------+----------+--------+----------+
newsrating
+----+----------+--------+--------+
| Id | NewsGuid | UserId | Rating |
+----+----------+--------+--------+
usernews
+----+----------+--------+----------+
| Id | NewsGuid | UserId | ReadDate |
+----+----------+--------+----------+

Newsitem显然包含newsitems,newsrating包含用户为newsitems提供的评级,而usernews包含用户阅读newsitem的日期。

在我的查询中,我想获取每个newsitem,包括该newsitem的评级数和平均评级,以及当前用户读取newsitem的次数。

到目前为止我所拥有的是:

select newsitem.guid, supplier, count(newsrating.id) as numberofratings,
    avg(newsrating.rating) as rating,
    count(case usernews.UserId when 3 then 1 else null end) as numberofreads from newsitem
    left join newsrating on newsitem.guid = newsrating.newsguid
    left join usernews on newsitem.guid = usernews.newsguid
    group by newsitem.guid

我在这里创建了一个sql小提琴:http://sqlfiddle.com/#!9/c8add/8

count()调用都不会返回我想要的数字。 numberofratings应该返回该newsitem的评级总数(由所有用户)。 numberofreads应返回该newsitem的当前用户的读取次数

因此,带有guid d104c330-c319-40e8-8be3-a7c4f549d35c的newsitem对于userid = 3的当前用户应该有2个等级和3个读取。

我尝试了条件计数和总和,但还没有成功。如何实现这一目标?

2 个答案:

答案 0 :(得分:2)

我看到的主要问题是你将两个表加在一起,这意味着你将有效地乘以两个数字,这就是为什么你的计数不正确。例如,如果Newsitem已被用户读取3次并被8位用户评分,那么您最终将获得24行,因此它看起来已被评为24次。您可以在DISTINCT的评分ID中添加COUNT,以便更正该问题。平均值应该不受影响,因为1和2的平均值与1,1,2和1的平均值相同。 2(例如)。

然后,您可以通过将用户ID添加到JOIN条件来处理读取(因为它是OUTER JOIN它不应导致任何结果丢失)而不是CASE语句对于COUNT,您可以对来自Usernews的不同ID值执行COUNT。生成的查询将是:

SELECT
    I.guid,
    I.supplier,
    COUNT(DISTINCT R.id) AS number_of_ratings,
    AVG(R.rating) AS avg_rating,
    COUNT(DISTINCT UN.id) AS number_of_reads
FROM
    NewsItem I
LEFT OUTER JOIN NewsRating R ON R.newsguid = I.guid
LEFT OUTER JOIN UserNews UN ON
    UN.newsguid = I.guid AND
    UN.userid = @userid
GROUP BY
    I.guid,
    I.supplier

虽然这应该有用,但是你可以从子查询中获得更好的结果,因为上面需要分解结果然后聚合它们,可能是不必要的。此外,有些人可能会发现下面的内容更清晰。

SELECT
    I.guid,
    I.supplier,
    R.number_of_ratings,
    R.avg_rating,
    COUNT(*) AS number_of_reads
FROM
    NewsItem I
LEFT OUTER JOIN
(
    SELECT
        newsguid,
        COUNT(*) AS number_of_ratings,
        AVG(rating) AS avg_rating
    FROM
        NewsRating
    GROUP BY
        newsguid
) R ON R.newsguid = I.guid
LEFT OUTER JOIN UserNews UN ON UN.newsguid = I.guid AND UN.userid = @userid
GROUP BY
    I.guid,
    I.supplier,
    R.number_of_ratings,
    R.avg_rating

答案 1 :(得分:2)

我和Tom一起使用子查询来计算用户数。

<强> SQL Fiddle Demo

SELECT NI.guid,
       NI.supplier,
       COUNT(NR.ID) as numberofratings, 
       AVG(NR.rating) as rating,
       user_read as numberofreads 
FROM newsitem NI
LEFT JOIN newsrating NR
       ON NI.guid = NR.newsguid
LEFT JOIN (SELECT NewsGuid, COUNT(*) user_read
           FROM usernews
           WHERE UserId = 3   -- use a variable @user_id here
           GROUP BY NewsGuid) UR
       ON NI.guid = UR.NewsGuid
GROUP BY  NI.guid,
          NI.supplier, 
          numberofreads;