答案 0 :(得分:2)
首先让我们说明问题。我们希望每个类别的所有电影评级最高。然后,在那些,我们想要最低的价格。
首先获得最高收视率
SELECT * FROM Films
INNER JOIN
(SELECT Max(Rating) as Rating, Category
FROM Films AS FM1 INNER JOIN Category AS C1 ON C1.CategoryId = FM1.CategoryId
GROUP BY Category
) x on Films.Rating = x.Rating and Films.Category = x.Category
现在,从中获得最便宜的价格
SELECT * FROM Films INNER JOIN
(SELECT Min(DVDPrice), x.Rating, Category FROM
(SELECT * FROM Films INNER JOIN
(SELECT MAX(Rating) as Rating, Category
FROM Films AS FM1 INNER JOIN Category AS C1 ON C1.CategoryId = FM1.CategoryId
GROUP BY Category
) x on Films.Rating = x.Rating and Films.Category = x.Category
)
WHERE DVDPrice IS NOT NULL
GROUP BY Category, DVDPrice
) y on Films.Rating = y.Rating and Films.Category = y.Category and Films.DVDRating = y.DVDRating
答案 1 :(得分:2)
what you want is:
-----------------
for each category, retrieve a film that meets the following 2 conditions:
的 _condition1:
{____ {1}} _
_ 强>rating= max rating in that category
{____ {1}} _
condition2:
price= min price in that category for films verifying condition 1
1解决方案是:
-->
OR:
in other terms it's equivalent to order films by Rating Desc then DVDPrice Asc for each category and take the first one.
使用您的数据,我已经完成了一些测试,看起来以下查询比上面的2更好:
SELECT FilmName, Rating, DVDPrice, Category
FROM Films FM1 INNER JOIN Category AS C1 ON C1.CategoryId = FM1.CategoryId
WHERE FM1.FilmId = (SELECT TOP 1 FilmId
FROM Films AS FM2
WHERE FM2.CategoryId = FM1.CategoryId
ORDER BY Rating DESC, DVDPrice)
答案 2 :(得分:1)
1)是的,您提供的第二个查询看起来更好。但我给@Russell Steen的解决方案+1,因为它避免使用相关子查询。
这是我经常在SO上看到的每组最大问题的变体。 这是另一种可能的解决方案:
SELECT f.*
FROM Films f
LEFT OUTER JOIN Films p
ON (f.CategoryId = p.CategoryId AND f.DVDPrice > p.DVDPrice)
LEFT OUTER JOIN Films r
ON (f.CategoryId = r.CategoryId AND f.DVDPrice = r.DVDPrice AND f.Rating < r.Rating)
WHERE p.CategoryId IS NULL AND r.CategoryId IS NULL;
解释是我们试图以较低的价格在同一类别中找到一部电影“p
”。当我们找不到时,p.*
将为NULL,因为外连接的工作方式。当没有价格较低的DVD时,我们找到了价格最低的DVD。
我们进一步尝试使用同样的技巧来找到评分最高的电影“r
”。这次我们以与电影f
相同的价格(即最低价格)限制同一类别和中的电影。否则我们会无意中发现该类别中评分最高的电影,即使它不便宜。
您还可以撤消加入顺序,首先找到最高评级,然后在具有最高评级的人中找到最低价格。这取决于您更优先考虑的因素 - 低价格或高评级。无论您使用何种解决方案,都必须对此优先级做出决定。
2)您尝试的另一个查询不起作用,因为您在子查询中使用的条件不会消除FT2子查询的任何错误行。这是一个“绿色鸡蛋和火腿”的问题:无论是在火车上,飞机上,船上还是山羊上,你都会在餐中加入绿色鸡蛋和火腿。
更新:好的,感谢您提供样本数据。当您第一次提出问题时,您没有包含某些电影可能不合格的信息,因为它们在DVD上不可用且在DVDPrice
列中有NULL。这是一个使用我的技术的更新查询,返回正确的电影,每个类别一个,不包括DVD上没有的电影,价格最低,评级最高:
SELECT f.FilmName, f.Rating, f.DVDPrice, f.CategoryId
FROM Films f
LEFT OUTER JOIN Films p ON (f.CategoryId = p.CategoryId
AND p.AvailableOnDvd = 'Y' AND f.DVDPrice > p.DVDPrice)
LEFT OUTER JOIN Films r ON (f.CategoryId = r.CategoryId
AND r.AvailableOnDvd = 'Y' AND f.DVDPrice = r.DVDPrice AND f.Rating < r.Rating)
WHERE f.AvailableOnDvd = 'Y' AND p.CategoryId IS NULL AND r.CategoryId IS NULL
ORDER BY f.CategoryId;
输出:
+-------------------------+--------+----------+------------+
| FilmName | Rating | DVDPrice | CategoryId |
+-------------------------+--------+----------+------------+
| The Maltese Poodle | 1 | 2.99 | 1 |
| Third | 7 | 10.00 | 2 |
| Nightmare on Oak Street | 2 | 9.99 | 3 |
| Planet of the Japes | 5 | 12.99 | 4 |
| Soylent Yellow | 5 | 12.99 | 5 |
| Sense and Insensitivity | 3 | 15.99 | 6 |
+-------------------------+--------+----------+------------+
这与第6类中的结果不同,因为样本数据中的 Sense and Insensitivity 是DVD上唯一可用的影片。 15 Late afternoon 不可用,即使它具有DVDPrice的非null值。如果我将其更改为AvailableOnDvd='Y'
,则选择 15 Late afternoon 而不是其他电影。
关于我如何解决这个问题,这是SQL中常见问题的变体,我已经标记了“每组最大n个”问题。您希望查询返回每部电影f
,以便不存在同一类别中较低DVDPrice
的电影。我使用p
的外部联接解决,如果p
中找不到匹配项,则f
必须具有该类别中的最低价格。这是常见的解决方案。
此问题的另一个问题是您有另一个要过滤的属性。因此,对于具有最低价格的电影(或关系中的电影),您需要具有最高评级的电影。该技术是相同的,使用外部联接到r
类别和价格相等,并且评级更高。如果找不到具有更高评级的此类电影,则f
必须具有给定类别和价格的最高评级。
我将在您的问题greatest-n-per-group
中添加一个标记,以便您可以关注它并查看使用相同技术解决的其他SQL问题。