2016-01-07 68 views
2

我找不到我的问题在这里的答案在stackoverflow。我有一个跨越3个表的查询:加入的sql条件计数

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

两个计数()调用不返回我想要的号码。 numberofratings应该返回评级为newsitem总数(所有用户)。 numberofreads应该为当前用户返回该新站点的读数的数量。

所以,newsitem与GUID d104c330-c319-40e8-8be3-a7c4f549d35c应该有2个等级和3读取与用户ID = 3

我曾尝试有条件数和金额的当前用户,但还没有成功。这如何实现?

+0

以及格式化问题。但下一次我可能会建议你使用一个整数ID而不是一个GUID,这个想法是让问题更简单。此外,如果可能的话,你应该在表格格式 –

+1

中包含你想要的输出和提供的数据,问题似乎是这两个左连接都增加了计数返回的行数(它基本上结合了加入a和加入b的选项) ,这导致了结果numberofreads和评级数量都是实际值的总和。所以我想你要么必须使用存储的数据库函数,要么使用子查询而不是左连接中的一个 –

+0

@JuanCarlosOropeza,我意识到这些GUID会让问题变得难以阅读。在撰写问题时,从我这边来一点懒惰:)关于所需的输出:我认为这个问题已经太长了,这就是为什么我决定将实际数据保存在sql小提琴中的原因。尽管如此,可能会更清楚。 – Moolie

回答

2

我看到的主要问题是你将两个表加在一起,这意味着你将有效地乘以两个数字,这就是为什么你的计数不会是正确的。例如,如果Newsitem已被用户阅读3次和8个用户,那么你将最终得到24行评级,所以它看起来像它已被评为24倍。您可以添加DISTINCT到你的收视率ID的COUNT并应纠正问题。平均值应不受影响,因为图1和2的平均相同的平均的1,1,2,& 2(例如)。

然后,您可以处理由在CASE声明添加用户ID到JOIN条件(因为它是一个OUTER JOIN它不应该导致结果的任何损失),而不是为你COUNT的读取,那么你可以做一个COUNT来自用户新闻的不同ID值。所得到的查询是:

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 
+0

我确定我们的查询非常相似,但你的第二个没有包含'@ user_id'或其他东西来过滤当前用户。 –

+0

谢谢。我把它放在第一个查询中,但在第二个查询中忘记了它。我现在就添加它。按照您的建议,将硬编码3更改为变量可能也更清晰。 –

+0

它永远不会让我惊讶,人们能够以多快的速度提出正确和详尽的答案。非常感谢汤姆。 – Moolie

2

我与汤姆你应该使用子查询来计算用户数。

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; 
+0

供参考。要解决此问题,您需要将问题分成较小的部分。我开始删除一些东西来查看结果中的数据,首先是“GROUP BY”,然后是第二个“LEFT JOIN”,然后我意识到问题出在哪里,并找到解决方法,在这种情况下是子查询。 –