【问题标题】:SQL - Subquery in Aggregate FunctionSQL - 聚合函数中的子查询
【发布时间】:2013-04-25 10:20:59
【问题描述】:

我正在使用 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

嗯...事实证明,聚合函数中不允许子查询。我已阅读有关此问题的其他帖子,但找不到适合我的特定用例的解决方案。希望你能帮帮我...

【问题讨论】:

  • 您使用的是哪个版本的 RDBMS?此外,您的问题也不正确,因为订单可以属于多个类别。
  • 我使用的是 MSSQL 2012。如果您引用子查询,则有一个 WHERE 子句 - WHERE c.categoryID = p2.CategoryID - 应该只过滤一个类别。

标签: sql subquery sql-server-2012 aggregate-functions northwind


【解决方案1】:

聚合函数中通常不允许子查询。相反,将聚合移入子查询。在这种情况下,由于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

【讨论】:

  • 感谢您完成了这项工作!仅供参考... CategoryID 也需要分组。最后一行应该类似于GROUP BY c.CategoryName, c.CategoryID。但是感谢您的时间,这个查询对我来说真的很奇怪。现在需要深入分析一下... :)
【解决方案2】:

使用CTEROW_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

【讨论】:

    【解决方案3】:

    这里绝对是一个子查询问题,这是一个excellent article(最初是为 Access 编写的,但语法相同),orderdate = 1997 也会给出 1997 年 1 月 1 日的订单日期——你需要 datepart(year, orderdate) = 1997,一旦为每个类别返回(最多五)行,您就可以封装返回的行并将它们聚合

    【讨论】:

    • 感谢您的链接。你说得对,我编辑了我的帖子。它应该是 ofc YEAR(o.OrderDate) = '1997'
    • 您能否提供一个“封装返回的行并聚合它们”的示例?
    • SELECT x.A, x.B, x.C, SUM(x.d) AS D FROM (任何有效的 sql select 语句包含列 a, b, c, d, e) x GROUP BY x.d
    • 这也是一个子查询,但它是一个非常简单的子查询,按类别子查询分组的前 5 个是更高级和更强大的用途。我不想通过使用相同的描述性现在来混淆。更重要的是要了解子查询的全部功能。
    【解决方案4】:

    我在 Access 子查询中遇到了一个非常类似的问题,其中记录按日期排序。当我使用“Last”聚合函数时,我发现它通过了所有子查询并从 Access 表中检索到最后一行数据,而不是按预期排序的查询。虽然我可以重写查询以在第一组括号中使用聚合函数(如前所述),但我发现将查询结果保存为数据库中按我想要的顺序排序的表更容易,然后使用“最后一个" 聚合函数来检索我想要的值。我将在未来运行更新查询以保持结果最新。效率不高但有效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-27
      • 1970-01-01
      • 1970-01-01
      • 2019-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多