2

从这里可用的 sakila 数据库中,我想在每个类别中找到接下来 5 部最长的电影。我提出了这个问题。使用查询

SELECT fl.title, fl.length, ct.name
FROM film fl JOIN film_category fc ON fl.film_id=fc.film_id
             JOIN category ct ON ct.category_id=fc.category_id
WHERE fl.length > (SELECT AVG(fl.length) 
                   FROM film fl JOIN film_category fc ON fl.film_id = fc.film_id
                   JOIN category cat ON cat.category_id=fc.category_id
                   WHERE cat.name=ct.name);

我可以找到所有类别中长度大于电影平均长度的电影。当我添加LIMIT 5时,只检索到 5 行 - 我需要从每个类别中获取 5 行。仅添加GROUP BY ct.name16 行 - 每个类别一个。

  1. 什么查询可以完成这项工作?
  2. 是否可以在不使用任何相关子查询的情况下实现这一目标?
  3. 什么可以帮助我确定关联子查询的结果是否可以通过加入表来实现?

我认为它与类似,但我无法找到解决方案。

谢谢!

4

1 回答 1

1

以下已针对 Sakila 数据库进行了测试(成功)...

SELECT title,
       length,
       categoryName
FROM ( SELECT CASE
                  WHEN film_category.category_id = @currentCategory THEN
                      @currentRecord := @currentRecord + 1
                  ELSE
                      @currentRecord := 1 AND
                      @currentCategory := film_category.category_id
              END AS recordNumber,
              film.title AS title,
              film.length AS length,
              categoryName AS categoryName
       FROM film
       JOIN film_category ON film.film_id = film_category.film_id
       JOIN ( SELECT film_category.category_id AS category_id,
                     category.name AS categoryName,
                     AVG( film.length ) AS categoryAvgLength
              FROM film
              JOIN film_category ON film.film_id = film_category.film_id
              JOIN category ON category.category_id = film_category.category_id
              GROUP BY film_category.category_id,
                       category.name
            ) AS categoryAvgLengthFinder ON film_category.category_id = categoryAvgLengthFinder.category_id,
            (
                SELECT @currentCategory := 0 AS currentCategory
            ) AS currentValuesInitialiser
       WHERE film.length > categoryAvgLength
       ORDER BY film_category.category_id,
                film.length
     ) AS allLarger
WHERE recordNumber <= 5;

该语句首先使用以下子查询形成一个“表”,其中包含每个category的唯一标识符category_id、该namecategory和相应的平均值Film length...

SELECT film_category.category_id AS category_id,
       category.name AS categoryName,
       AVG( film.length ) AS categoryAvgLength
FROM film
JOIN film_category ON film.film_id = film_category.film_id
JOIN category ON category.category_id = film_category.category_id
GROUP BY film_category.category_id,
       category.name

然后将这个子查询的结果连接到filmfilm_catgory。由于子查询从中检索category语句其余部分所需的所有详细信息,因此不需要JOINwith category

然后将生成的数据集交叉连接以在同一语句SELECT @currentCategory := 0 AS currentCategory中初始化变量。@currentCategory这确实是以将调用的字段附加到上面生成的数据集为代价的currentCategory,因此您可能更喜欢使用以下代码...

SET @currentCategory := 0;
SELECT title,
       length,
       categoryName
FROM ( SELECT CASE
                  WHEN film_category.category_id = @currentCategory THEN
                      @currentRecord := @currentRecord + 1
                  ELSE
                      @currentRecord := 1 AND
                      @currentCategory := film_category.category_id
              END AS recordNumber,
              film.title AS title,
              film.length AS length,
              categoryName AS categoryName
       FROM film
       JOIN film_category ON film.film_id = film_category.film_id
       JOIN ( SELECT film_category.category_id AS category_id,
                     category.name AS categoryName,
                     AVG( film.length ) AS categoryAvgLength
              FROM film
              JOIN film_category ON film.film_id = film_category.film_id
              JOIN category ON category.category_id = film_category.category_id
              GROUP BY film_category.category_id,
                       category.name
            ) AS categoryAvgLengthFinder ON film_category.category_id = categoryAvgLengthFinder.category_id
       WHERE film.length > categoryAvgLength
       ORDER BY film_category.category_id,
                film.length
     ) AS allLarger
WHERE recordNumber <= 5;

一旦JOIN's 被执行(并被@currentCategory初始化),结果数据集将被细化为那些其值为film.length' is greater than the corresponding average for that category. The refined dataset is then sorted (not grouped) by one of thecategory_id fields (of which there will be two sharing the same value owing to the joining) and subsorted byfilm.length` 的记录。

选择字段后,每条记录的值category_id都会与 的值进行比较@currentCategory。如果它们不匹配,则@currentRecord初始化为1@currentCategory更新为 的新值category_id。如果它们匹配,则@currentRecord递增。在任何一种情况下,分配给的值@currentRecord都会返回到SELECT给定别名的字段中的语句中recordNumber。因此,我们能够在我们精炼的数据集前面加上一个记录号。

然后剩下的就是SELECT精炼数据集中的所有记录(无记录号),其中记录号小于或等于5

请注意,上面的结果没有使用任何相关的子查询,而是使用JOIN's to 表和子查询。

如果您有任何问题或意见,请随时发表相应的评论。

于 2017-04-26T17:15:10.490 回答