以下查询是否更正了问题?

时间:2009-11-05 19:39:12

标签: sql tsql greatest-n-per-group

3 个答案:

答案 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问题。