从窗口函数中选择最佳结果的最简单方法?

时间:2015-04-20 18:31:17

标签: sql sql-server

所以,假设我有一个客户列表,我想为所有客户选择详细信息,以及从特定类别的产品中购买最多的产品。即使他们没有购买这些产品中的一种,我也想选择客户的详细信息,而只是为他们从该类购买的产品中显示null。

我将从以下开始作为CTE或临时表:

SELECT
 CUST_NUMBER
,PRODUCT
,ROW_NUMBER() OVER (PARTITION BY CUST_NUMBER ORDER BY COUNT(ORDER_NUM) DESC) [ProdRank]
FROM ORDERS
WHERE PROD_CLASS = 'x'
GROUP BY 
 CUST_NUMBER
,PRODUCT

事情是这样的 - 在这个产品类中可以有许多不同的产品,我只对选择ProdRank = 1的位置感兴趣。但是你可能知道,我不能在ProdRank的WHERE或HAVING子句中指定到= 1。

我收到错误消息“窗口函数只能出现在SELECT或ORDER BY子句中。”

由于许多客户可能尚未在产品类别中订购任何产品,因此情况进一步复杂化。因此我不能简单地将客户列表加入到上面并指定WHERE ProdRank = 1,否则它模仿内部联接并且我放弃任何ProdRank为空的客户。

为了解决这个问题,我提出的方法是首先使用上面的代码创建一个临时表作为#Products,其中包括客户和具有相应排名的每个产品。然后我创建了一个名为#TopProducts的第二个临时表,其中我只是:

SELECT * FROM 
#Products WHERE 
ProdRank = 1

之后我就离开了来自我的Customers表的#TopProducts。

似乎应该有一种更简单的方法来解决这个问题。有没有什么办法可以在一步中选择ROW_NUMBER()或RANK()的顶级分区结果,而不是创建两个临时表?

2 个答案:

答案 0 :(得分:2)

使用Common Table Expression

WITH topProducts AS (
  SELECT
   CUST_NUMBER
  ,PRODUCT
  ,ROW_NUMBER() OVER (PARTITION BY CUST_NUMBER ORDER BY COUNT(ORDER_NUM) DESC) [ProdRank]
  FROM ORDERS
  WHERE PROD_CLASS = 'x'
  GROUP BY 
   CUST_NUMBER
  ,PRODUCT
)
SELECT *
FROM CustomerDetails c
LEFT JOIN TopProducts p ON (ProdRank = 1 AND c.CUST_NUMBER = p.CUST_NUMBER)

使用子查询:

SELECT *
FROM CustomerDetails c
LEFT JOIN (
  SELECT
   CUST_NUMBER
  ,PRODUCT
  ,ROW_NUMBER() OVER (PARTITION BY CUST_NUMBER ORDER BY COUNT(ORDER_NUM) DESC) [ProdRank]
  FROM ORDERS
  WHERE PROD_CLASS = 'x'
  GROUP BY 
   CUST_NUMBER
  ,PRODUCT
) p ON (ProdRank = 1 AND c.CUST_NUMBER = p.CUST_NUMBER)

答案 1 :(得分:0)

我会在你的场景中使用外部应用和顶部。那有意义吗? 这里很少有例子Real life example, when to use OUTER / CROSS APPLY in SQL

我会写一段代码,但是我在移动设备上并且真的不舒服......