多个联接,一个表上的平均值,指望另一个

时间:2016-09-16 17:40:24

标签: sql sql-server performance tsql

我在数据库中有四个表:City,User,CityRating,CityGreeting。 CityRating表具有UserID和CityID作为PK,它们是USer和City表的FK。 CityGreeting表没有PK,但是UserID和CityID是FK(想法是用户可以根据需要多次问候城市,但只对城市评分一次)。

我正在尝试编写一个查询,它将返回整个城市的平均评分,以及特定用户对该城市的欢迎时间:

select City.CityID, City.CityName, City.CityStateOrProvince, 
ROUND(AVG(Cast(RateCity.Rating as float)), 2) as AverageRating,
(select COUNT(HelloCity.CityID) from HelloCity where HelloCity.UserID like '<guid>') as TimesVisited
from City
right join RateCity
on City.CityID = RateCity.CityID
right join HelloCity
on City.CityID = HelloCity.CityID
group by City.CityID, City.CityName,
City.CityStateOrProvince, City.CityCountry, City.CityImageUri

即使我能按预期工作(目前还没有),我觉得它真的很乱。就最佳实践而言,编写两个查询会更好吗?这个操作将在api中执行,不确定在编写两个单独的查询时性能是否会更好,或者像这样复杂的一个。有关此问题的任何见解或如何使查询按预期工作?

***编辑:添加图片以澄清:平均评分是所有评分用户的平均评分,而TimesVisited是特定用户访问该城市的次数。

Added picture to clarify: Average Rating is the average of all users who rated, and TimesVisited is the amount of times one specific user has visited the city.

2 个答案:

答案 0 :(得分:3)

我认为除了city之外,您需要分别汇总表格才能使其正常运行:

select c.*, rc.AverageRating, coalesce(hc.TimesVisited, 0) as TimesVisited
from City c join
     (select CityId, ROUND(AVG(Cast(RateCity.Rating as float)), 2) as AverageRating
      from RateCity rc
      group by CityId
     ) rc
     on c.CityID = rc.CityID left join
     (select CityId, count(*) as TimesVisited
      from HelloCity hc
      where hc.UserID like '<guid>'
      group by CityId
     ) hc
     on c.CityId = hc.CityId;

注意:

  • 表别名使查询更易于编写和阅读。
  • 我怀疑你的意思是right join。这意味着其他两个表中的CityId不在City中。
  • 通过对每个其他表进行聚合,您不需要在外部查询中进行聚合。
  • 我认为你想要left joinHelloCity,因为并非所有城市都有访问者。
  • 如果不是所有城市都有评分,您可能还需要left joinRateCity

答案 1 :(得分:2)

为什么不使用CTE然后在每个CTE中执行各个部分,这有助于将其分解而不是尝试将多个连接混合在一起:例如:

DECLARE @userId VARCHAR(10) = 'userid1' ;

WITH 

CITY_RATING_CTE (cityId, AverageRating) AS
  ( SELECT cityId,
           AVG(Rating) AS rating
   FROM RateCity
   GROUP BY cityId),

TIMES_VISITED_CTE AS
  ( SELECT cityId,
           count(*) AS TimesVisited
   FROM HelloCity
   WHERE UserId = @userId
   GROUP BY cityId)
SELECT c.CityId,
       c.CityName,
       c.CityStateOrProvince,
       c.CityImageUri,
       cr.AverageRating,
       tv.TimesVisited
FROM City c
JOIN CITY_RATING_CTE cr ON cr.cityId = c.CityId
JOIN TIMES_VISITED_CTE tv ON cr.cityId = cr.cityId;