根据另一个表中的信息排序查询结果

时间:2014-07-07 21:56:17

标签: sql sql-server

我有一个名为workouts的表和另一个名为likesWorkouts的表。如果用户喜欢或不喜欢锻炼,那么我将用户ID,喜欢的锻炼的锻炼ID以及真或假插入名为likesWorkouts的表中。数据库结构与此问题SQL database structure for Like and DisLike

中接受的答案相同

我正在尝试从锻炼表中检索锻炼,这些锻炼按照每个人的喜欢数量排序,但由于没有喜欢列,而且我可以获得锻炼的数量的唯一方法是做COUNT( *)在preferworkouts表中查询我应该按照他们喜欢的数量订购锻炼表吗?

修改

所以我让它工作唯一的问题是我有另一个名为Downloads的列表有两列userIDWorkoutID我需要获得一个训练的下载量,所以我添加了{{1}你的替代评级系统查询,但它导致喜欢和不喜欢的价值是错误的。知道为什么吗?

EDIT2:

所以我可以通过此查询获得下载

LEFT JOIN DownloadedWorkouts AS d ON w.ID = d.WorkoutID

如何将此添加到您的备用查询中?

2 个答案:

答案 0 :(得分:7)

SELECT w.WorkoutID, COUNT(*) AS likeCount
  FROM Workouts AS w
  JOIN LikedWorkouts AS l
    ON w.WorkoutID = l.WorkoutID
 WHERE l.liked = TRUE  -- You might need an alternative spelling of TRUE
 GROUP BY w.WorkoutID
 ORDER BY likeCount DESC;  -- You might need ORDER BY COUNT(*) DESC

这并不能解释这些不喜欢的事情;排名最高的结果可能是一个有1000个喜欢和5000个不喜欢(其中一个有300个喜欢和0个不喜欢)。如果你想分别报告喜欢和不喜欢,或喜欢减去不喜欢的百分比,或喜欢与不喜欢的百分比,那么你必须修改查询,有时候从根本上适度。

这也没有显示任何根本不喜欢的锻炼(因为LikedWorkouts中的所有值都是FALSE,或者因为没有人对锻炼发表了意见)。有一些方法可以解决这个问题,但如果需要的话,需要更清楚地说明规范。


更完整的排名功能规格似乎是“喜欢的数量”除以“喜欢的数量加上不喜欢的数量”,其中没有喜欢或不喜欢的锻炼有0个喜欢和0个不喜欢(以及与没有喜欢或不喜欢的人会被视为有1000个不喜欢和不喜欢的锻炼。

为此,我倾向于使用TDQD - 测试驱动的查询设计。

计算每个喜欢或不喜欢的锻炼的喜欢和不喜欢的数量

SELECT WorkoutID,
       SUM(CASE WHEN liked THEN 1 ELSE 0 END) AS likes,
       SUM(CASE WHEN liked THEN 0 ELSE 1 END) AS dislikes
  FROM LikedWorkouts
 GROUP BY WorkoutID

在LikedWorkouts中没有条目的情况下为锻炼生成0喜欢和不喜欢

这往往是特定于DBMS的,而SQL Server不是我的主要平台。最简单的方法是使用NVL或IFNULL等函数(以及LEFT JOIN)。 SQL Server似乎使用了IFNULL。

SELECT w.WorkoutID,
       IFNULL(SUM(CASE WHEN l.liked THEN 1 ELSE 0 END), 0) AS likes,
       IFNULL(SUM(CASE WHEN l.liked THEN 0 ELSE 1 END), 0) AS dislikes
  FROM Workouts AS w
  LEFT JOIN LikedWorkouts AS l ON w.WorkoutID = l.WorkoutID
 GROUP BY WorkoutID

计算喜欢的分数

SELECT WorkoutID, likes / (likes + dislikes) AS rating
  FROM (SELECT w.WorkoutID,
               IFNULL(SUM(CASE WHEN l.liked THEN 1 ELSE 0 END), 0) AS likes,
               IFNULL(SUM(CASE WHEN l.liked THEN 0 ELSE 1 END), 0) AS dislikes
          FROM Workouts AS w
          LEFT JOIN LikedWorkouts AS l ON w.WorkoutID = l.WorkoutID
         GROUP BY WorkoutID
       ) AS r
 ORDER BY 2;  -- Or rating, or likes / (likes + dislikes)

有一个待解决的剩余问题 - 当给定锻炼没有评级时,除以零。

替代排名

在某些方面,更好的排名功能会从喜欢的数量中减去不喜欢的数量,并除以喜欢和不喜欢的计数之和。然后没有排名的训练将在中间,排名为0%;只有不喜欢投票的锻炼将是-100%;而只有投票的锻炼将是+ 100%。只有最后一个查询需要更改,并且它生成的评级不是百分比,而只是一小部分-1.00 .. + 1.00。

SELECT WorkoutID, (likes - dislikes) / (likes + dislikes) AS rating
  FROM (SELECT w.WorkoutID,
               IFNULL(SUM(CASE WHEN l.liked THEN 1 ELSE 0 END), 0) AS likes,
               IFNULL(SUM(CASE WHEN l.liked THEN 0 ELSE 1 END), 0) AS dislikes
          FROM Workouts AS w
          LEFT JOIN LikedWorkouts AS l ON w.WorkoutID = l.WorkoutID
         GROUP BY WorkoutID
       ) AS r
 ORDER BY 2;  -- Or rating, or (likes - dislikes) / (likes + dislikes)

这有相同的除以零问题需要解决。

将除数固定为零

我认为(未经测试)你可以用以下方法解决除零问题:

SELECT WorkoutID,
       CASE
       WHEN (likes + dislikes) != 0
       THEN (likes - dislikes) / (likes + dislikes)
       ELSE 0
       END AS rating
  FROM (SELECT w.WorkoutID,
               IFNULL(SUM(CASE WHEN l.liked THEN 1 ELSE 0 END), 0) AS likes,
               IFNULL(SUM(CASE WHEN l.liked THEN 0 ELSE 1 END), 0) AS dislikes
          FROM Workouts AS w
          LEFT JOIN LikedWorkouts AS l ON w.WorkoutID = l.WorkoutID
         GROUP BY WorkoutID
       ) AS r
 ORDER BY 2;  -- Or rating, or (likes - dislikes) / (likes + dislikes)

警告:所有SQL尚未在任何DBMS上测试,更不用说SQL Server了。

(注意:我通常避免ORDER BY 2这是1986年SQL标准的一部分。许多DBMS允许您按选择列表中的列别名排序;其他DBMS可能要求您重复选择表达式中的表达式ORDER BY子句中的列表(这是违反DRY(不要重复自己)原则的一个令人遗憾的情况。使用ORDER BY 2可能在大多数DBMS中起作用并绕过问题 - 但不是很理想。我不太清楚SQL Server是否足以知道它在这个问题上的影响范围。)


添加下载

现在的问题表明这是计算下载量的一种方式:

SELECT *
FROM (SELECT w.ID,
             IFNULL(SUM(CASE WHEN d.WorkoutID THEN 1 ELSE 0 END), 0) AS downloads
        FROM UserWorkouts AS w 
        LEFT JOIN Profiles ON w.CreatorID = Profiles.UserID
        LEFT JOIN DownloadedWorkouts AS d ON w.ID = d.WorkoutID
       GROUP BY w.ID
   ) AS r;

围绕必要的查询包装SELECT * FROM (...) AS r - 对于下载,您只需使用:

SELECT w.ID,
       IFNULL(SUM(CASE WHEN d.WorkoutID THEN 1 ELSE 0 END), 0) AS downloads
  FROM UserWorkouts AS w
  LEFT JOIN Profiles ON w.CreatorID = Profiles.UserID
  LEFT JOIN DownloadedWorkouts AS d ON w.ID = d.WorkoutID
 GROUP BY w.ID

但是,此查询有三个新表;没有一个表明显对应于原始问题中的表。也许UserWorkouts就是之前的Workouts。我不明白为什么会介绍个人资料;看起来您应该只使用所有锻炼列表和DownloadedWorkouts表。 (将来,请至少为问题中的表格提供骨架模式;这将简化每个人的工作 - 为您翻译答案,并为我们提供答案。)

SELECT w.WorkoutID,
       IFNULL(SUM(CASE WHEN d.WorkoutID THEN 1 ELSE 0 END), 0) AS downloads
  FROM Workouts AS w
  LEFT JOIN DownloadedWorkouts AS d ON w.WorkoutID = d.WorkoutID
 GROUP BY w.ID

请注意,我已经使用了Workouts表,并假设主键列与以前一样是WorkoutID。

将其与主查询相结合,得出:

SELECT r.WorkoutID, r.Rating, d.Downloads
  FROM (SELECT WorkoutID,
               CASE
               WHEN (likes + dislikes) != 0
               THEN (likes - dislikes) / (likes + dislikes)
               ELSE 0
               END AS rating
          FROM (SELECT w.WorkoutID,
                       IFNULL(SUM(CASE WHEN l.liked THEN 1 ELSE 0 END), 0) AS likes,
                       IFNULL(SUM(CASE WHEN l.liked THEN 0 ELSE 1 END), 0) AS dislikes
                  FROM Workouts AS w
                  LEFT JOIN LikedWorkouts AS l ON w.WorkoutID = l.WorkoutID
                 GROUP BY WorkoutID
               ) AS r1
       ) AS r
  JOIN (SELECT w.ID AS WorkoutID,
               IFNULL(SUM(CASE WHEN d.WorkoutID THEN 1 ELSE 0 END), 0) AS downloads
          FROM Workouts AS w
          LEFT JOIN DownloadedWorkouts AS d ON w.WorkoutID = d.WorkoutID
         GROUP BY w.ID
       ) AS d
    ON r.WorkoutID = d.WorkoutID
 ORDER BY r.rating, d.WorkoutID;

这是TDQD实施的一个很好的示范。随着开发的进行,您可以创建和测试各种子查询,即使生成的查询相当复杂,也会得到可靠的答案。我不想尝试从头开始写这个最终查询;事实上,我不会 - 我会使用TDQD来获得正确的部件,就像所示。

注意:我仍然未经测试所有SQL。

答案 1 :(得分:1)

这样的事情:

SELECT  w.*
FROM    workouts w
        LEFT OUTER JOIN (
                    SELECT  workoutID, 
                            cnt=COUNT(*)
                    FROM    likedWorkouts
                    WHERE   liked = 1
                    GROUP BY workoutID
                    ) wl ON wl.workoutID = w.workoutID
ORDER BY wl.cnt DESC