SQL SERVER OVER PARTITION 业务案例

Posted

技术标签:

【中文标题】SQL SERVER OVER PARTITION 业务案例【英文标题】:OVER PARTITION BY SQL SERVER Business case 【发布时间】:2015-06-12 09:31:07 【问题描述】:

我需要一些帮助来解决一个问题。我在 SQL SERVER 2012 工作。基本上是这样的:

客户可以购买三种产品:

    一个 B C

如果客户同一天,我们应该将其显示为“Dual”。

如果客户在同一天只购买了一件产品,那么我们应该称其为“单件”。

如果客户在同一天购买了所有三种产品,我们应该称之为“Triple”。

这是我的数据目前的样子:

   YearMonth   Product CustomerNr        Sales Date
    201505      B      70056844         20150501
    201505      A      70056844         20150501
    201505      B      70057297         20150503
    201505      A      70057494         20150504
    201505          B      70057494         20150504
    201505      C      70057494         20150504
    201505      B      70033055         20150506
    201505      B      36021632         20150508
    201505      A      70060612         20150508
    201505      C      70060612         20150508

正如我们在示例数据中看到的,客户编号:70060612 在同一销售日期购买了两种产品 A 和 C,因此我们可以说他购买了双包装。客户编号:70057494 在同一销售日期购买了 3 种产品,A、B 和 C。我们可以称之为 Triple。

我希望我的数据包含这样的列:

   YearMonth   Product CustomerNr        Sales Date  Package        
    201505      B      70056844         20150501    Dual
    201505      A      70056844         20150501    Dual
    201505      B      70057297         20150503    Single
    201505      A      70057494         20150504    Triple
    201505          B      70057494         20150504    Triple
    201505      C      70057494         20150504    Triple
    201505      B      70033055         20150506    Single
    201505      B      36021632         20150508    Single
    201505      A      70060612         20150508    Dual
    201505      C      70060612         20150508    Dual

我该怎么做?我正在考虑做一些类似的事情

 Test=COUNT(*) OVER (PARTITION BY CustomerNr)

【问题讨论】:

有一个“教科书示例”,使用您提到的方法。 MSDN 看例子B。 【参考方案1】:

这是一个带有相应数据示例的解决方案。希望这对您有所帮助。

create table #t(dt datetime, prod char(1), customer int)
declare @date datetime = getdate()

insert into #t(dt,prod,customer)
values(@date,N'A',1),(@date,N'A',1),(@date,N'C',1),(@date,N'B',2),(@date,N'C',2),(@date,N'B',3),(@date,N'A',3),(@date,N'A',4),(@date,N'B',5),(@date,N'C',6),
(@date,N'A',7),(@date,N'B',7),(@date,N'C',7)


SELECT dt,prod,customer, MAX(cntDay) OVER(PARTITION BY customer, dt) as cntDay, 
    CASE MAX(cntDay) OVER(PARTITION BY customer, dt)
        WHEN 2 THEN N'Dual'
        WHEN 3 THEN N'Triple'
        ELSE N'Single' END as package -- Your Case
FROM (
    SELECT *, DENSE_RANK() OVER(PARTITION BY customer, dt ORDER BY prod) as cntDay
    FROM #t
) as dat
ORDER BY customer, dt -- just for a better overview

drop table #t

最好的问候,离子

【讨论】:

您没有考虑实现单一、双重或三重销售所需的不同组合。您的解决方案会将同一客户在同一天对 A 的 2 次销售计为双重销售。 更正了演示。感谢您的提示。现在计数正确。【参考方案2】:

不幸的是 SQL 不允许 COUNT(Distinct) OVERDENSE_RANK() 是一种解决方法

DECLARE @Table table
(
    id int identity(1,1) primary key,
    YearMonth int,
    Product char(1),
    CustomerNr int,
    SalesDate date
)

INSERT @Table VALUES
(201505, 'B', 70056844, '20150501'),
(201505, 'A', 70056844, '20150501'),
(201505, 'B', 70057297, '20150503'),
(201505, 'A', 70057494, '20150504'),
(201505, 'B', 70057494, '20150504'),
(201505, 'C', 70057494, '20150504'),
(201505, 'B', 70033055, '20150506'),
(201505, 'B', 36021632, '20150508'),
(201505, 'A', 70060612, '20150508'),
(201505, 'C', 70060612, '20150508'),
(201505, 'A', 70056844, '20150501') -- Additional for duplicated product

SELECT 
    *, 
    CASE 
        DENSE_RANK() OVER (PARTITION BY CustomerNr, SalesDate ORDER BY Product)
         + DENSE_RANK() OVER (PARTITION BY CustomerNr, SalesDate ORDER BY Product DESC) - 1
        WHEN 1 THEN 'Single'
        WHEN 2 THEN 'Dual'
        WHEN 3 THEN 'Triple'
        ELSE 'Multiple' -- Not defined case 
    END AS Package
FROM 
    @Table
ORDER BY 
    id

结果

id          YearMonth   Product CustomerNr  SalesDate  Package
----------- ----------- ------- ----------- ---------- --------
1           201505      B       70056844    2015-05-01 Dual
2           201505      A       70056844    2015-05-01 Dual
3           201505      B       70057297    2015-05-03 Single
4           201505      A       70057494    2015-05-04 Triple
5           201505      B       70057494    2015-05-04 Triple
6           201505      C       70057494    2015-05-04 Triple
7           201505      B       70033055    2015-05-06 Single
8           201505      B       36021632    2015-05-08 Single
9           201505      A       70060612    2015-05-08 Dual
10          201505      C       70060612    2015-05-08 Dual
11          201505      A       70056844    2015-05-01 Dual

【讨论】:

我有一个问题。很好,你带了重复的行,ID 11。如果客户在同一天购买了 ID 2 和 ID 11,并且购买了相同的产品,但每个产品 (A) 的服务 ID 不同,怎么能限制让我仅在特定日期为每个产品提取一行客户编号?示例 no 应提取产品 A 的两行。每天每个产品只能一行。 @user3197575 是的,您始终可以在计算 Package 类型之前过滤表。 SELECT MIN(id) id, MIN(SalesDate) SalesDate, YearMonth, Product, CustomerNr FROM @Table GROUP BY YearMonth, Product, CustomerNr

以上是关于SQL SERVER OVER PARTITION 业务案例的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 10.50 OVER PARTITION 无法识别

SQL Server Rank() Over Partition w/Back and Forth 值

sql over表示啥意思

SQL Server - 带有 PARTITION 的 ROW_NUMBER(),如何获取多条记录?

SQL server PARTITION BY 是啥意思

SQL 聚合 OVER 和 PARTITION