SQL-подзапрос в агрегатной функции

Я использую базу данных northwind, чтобы обновить свои навыки SQL, создав несколько более или менее сложных запросов. К сожалению, я не смог найти решение для моего последнего варианта использования: "Получите сумму пяти самых больших заказов для каждой категории в 1997 году."

задействованные таблицы:

Orders(OrderId, OrderDate)
Order Details(OrderId, ProductId, Quantity, UnitPrice)
Products(ProductId, CategoryId)
Categories(CategoryId, CategoryName)

я попробовал следующий запрос

SELECT c.CategoryName, SUM(
  (SELECT TOP 5 od2.UnitPrice*od2.Quantity 
   FROM [Order Details] od2, Products p2
   WHERE od2.ProductID = p2.ProductID
   AND c.CategoryID = p2.CategoryID
   ORDER BY 1 DESC))
FROM [Order Details] od, Products p, Categories c, Orders o 
WHERE od.ProductID = p. ProductID
AND p.CategoryID = c.CategoryID
AND od.OrderID = o.OrderID
AND YEAR(o.OrderDate) = 1997
GROUP BY c.CategoryName

хорошо... Оказалось, что подзапросы в агрегатных функциях не допускаются. Я читал другие сообщения об этом проблема, но не может найти решение для моего конкретного случая использования. Надеюсь, вы сможете мне помочь...

4 ответов


подзапросы обычно не допускаются в агрегатных функциях. Вместо этого переместите агрегат внутри подзапрос. В этом случае вам понадобится дополнительный уровень подзапроса из-за top 5:

SELECT c.CategoryName,
  (select sum(val)
   from (SELECT TOP 5 od2.UnitPrice*od2.Quantity as val
         FROM [Order Details] od2, Products p2
         WHERE od2.ProductID = p2.ProductID
         AND c.CategoryID = p2.CategoryID
         ORDER BY 1 DESC
        ) t
  )
FROM [Order Details] od, Products p, Categories c, Orders o 
WHERE od.ProductID = p. ProductID
AND p.CategoryID = c.CategoryID
AND od.OrderID = o.OrderID
AND YEAR(o.OrderDate) = 1997
GROUP BY c.CategoryName, c.CategoryId

использовать CTE С функции row_number функция ранжирования вместо чрезмерного подзапроса.

 ;WITH cte AS
 (
  SELECT c.CategoryName, od2.UnitPrice, od2.Quantity,
         ROW_NUMBER() OVER(PARTITION BY c.CategoryName ORDER BY od2.UnitPrice * od2.Quantity DESC) AS rn
  FROM [Order Details] od JOIN Products p ON od.ProductID = p.ProductID
                          JOIN Categories c ON p.CategoryID = c.CategoryID
                          JOIN Orders o ON od.OrderID = o.OrderID
  WHERE o.OrderDate >= DATEADD(YEAR, DATEDIFF(YEAR, 0, '19970101'), 0)
    AND o.OrderDate < DATEADD(YEAR, DATEDIFF(YEAR, 0, '19970101')+1, 0)
  )
  SELECT CategoryName, SUM(UnitPrice * Quantity) AS val
  FROM cte
  WHERE rn < 6
  GROUP BY CategoryName

его определенно проблема под запросом здесь является отличная статья на этом (первоначально написано для доступа, но синтаксис идентичен), также orderdate = 1997 даст дату заказа для 1 января 1997 ' -- вам нужна datepart (год, orderdate) = 1997, как только у вас есть (до пяти) строк, возвращенных для каждой категории, вы можете инкапсулировать возвращенные строки и объединить их


Я столкнулся с очень похожей проблемой с подзапросом доступа, где записи были отсортированы по дате. Когда я использовал агрегатную функцию "Last", я обнаружил, что она прошла через все подзапросы и извлекла последнюю строку данных из таблицы Access, а не отсортированный запрос, как предполагалось. Хотя я мог бы переписать запрос, чтобы использовать агрегатную функцию в первом наборе скобок (как было предложено ранее) , мне было проще сохранить результаты запроса в виде таблицы в базе данных сортировка в порядке, который я хотел, а затем использовать" последнюю " агрегатную функцию для извлечения значений, которые я хотел. Я буду запускать запрос обновления в будущем, чтобы сохранить текущие результаты. Не эффективно, но эффективно.