【问题标题】:using partition by, group by and count together使用 partition by、group by 和一起计数
【发布时间】:2015-12-21 20:12:21
【问题描述】:

块引用 复制/粘贴数据:

productNumber   Account BuyDate SellDate    RelevantYYYYMM
27530   8605148 11/5/15 7:03 PM 11/11/15 2:02 PM    201511
27530   8582063 10/14/15 7:17 PM    10/16/15 1:48 PM    201510
27530   8566748 9/30/15 6:51 PM 10/13/15 2:09 PM    201510
101167  8622268 11/27/15 7:50 PM    11/27/15 8:35 PM    201511
101167  8622267 11/27/15 8:49 AM    11/27/15 11:02 AM   201511
101167  8622265 11/26/15 8:47 AM    11/26/15 11:14 AM   201511
101167  8622272 11/25/15 7:43 PM    11/25/15 8:40 PM    201511
102432  8611407 11/12/15 5:41 PM    11/25/15 11:50 AM   201511
102432  8600664 11/2/15 12:04 AM    11/4/15 12:56 PM    201511
102432  8573179 10/5/15 2:03 PM 10/7/15 3:47 PM 201510
127819  8581822 10/14/15 7:49 PM    10/18/15 10:46 AM   201510
127819  8572308 10/2/15 4:38 PM 10/4/15 2:28 PM 201510
127819  8571220 10/2/15 9:34 AM 10/2/15 4:25 PM 201510
141105  8612529 11/16/15 7:45 AM    11/16/15 3:06 PM    201511
141105  8612460 11/14/15 10:04 AM   11/14/15 12:48 PM   201511
141105  8608738 11/10/15 12:53 PM   11/10/15 3:19 PM    201511
141105  8603007 11/3/15 5:28 PM 11/9/15 3:54 PM 201511
141105  8595018 10/28/15 7:23 PM    11/2/15 2:09 PM 201511
141105  8562238 10/22/15 8:40 AM    10/27/15 5:11 PM    201510

我需要按 RelevantYYYYMM 计算 ProductNumber 组(201512 表示 2015 年 12 月)。但是,逻辑应该是:

  1. ProductNumber 及其最近的 BuyDate 应在该 RelevantYYYYMM 范围内。
  2. 与 ProductNumber 的最新 BuyDate 关联的先前 BuyDate 应在 30 天范围内。示例:产品 27530 的最近购买日期是 2015 年 11 月 5 日。产品之前的 BuyDate 是 10/14/15,即 11.5.15 后的 30 天内。
  3. 第 3 次购买日期应在第 2 次购买日期后的 30 天内。示例:ProductNumber 27530 的第二个 BuyDate 是 10/14/15,第三个 BuyDate 是 9/30/15,在 10/14/15 的 30 天范围内。
  4. 应对每个 ProductNumber 和与该编号关联的 RelevantYYYYMM 进行此验证。示例:对于 RelevantYYYYMM 201510(2015 年 10 月),ProductNumber 27530 不应出现在我的计数中,因为未满足 Stpes 1-3。说明n:201510(2015 年 10 月)中 ProductNumber 27530 的最近日期是 2015 年 10 月 14 日。之前的 BuyDate 是 2015 年 9 月 30 日,即 2015 年 10 月 14 日的 30 天范围内。但是,该 ProductNumber 在 2015 年 9 月 30 日之前没有另一个在 30 天范围内的 BuyDate。它无法验证第 3 步,因此从计数中排除。

代码:

--get all products and assign YYYYMM
drop table #firstbuy
declare @StartDate as date  = '01/01/2015'
declare @EndDate as date = '12/31/2015'

select ProductNumber, Account, BuyDate, SellDate,
CAST(DATEPART(yyyy, BuyDate) AS varchar(4))+ 
CAST(REPLICATE('0', 2-LEN(MONTH(BuyDate))) AS varchar(2)) +
CAST(DATEPART(MONTH, BuyDate) AS varchar(2)) AS RelevantYYYYMM
into #firstbuy
From products V
WHERE
V.BuyDate BETWEEN @StartDate AND @EndDate 
--select * from #firstbuy

--Rank by BuyDate desc.  Use ProductNumber and RelevantYYYYMM for partition     by
drop table #firstbuypart2
select * 
into #firstbuypart2
from (
select ProductNumber, Account, BuyDate, SellDate, RelevantYYYYMM,
                ROW_NUMBER() OVER (PARTITION BY ProductNumber,     RelevantYYYYMM ORDER BY BuyDate DESC) AS     getcount1                               
from #firstbuy
) a
--select * from #firstbuypart2 

--Grab records that has count = 1.  This will give you most recent BuyDate per RelevantYYYYMM
drop table #firstbuypart3
select ProductNumber, BuyDate, SellDate, RelevantYYYYMM 
into #firstbuypart3
from #firstbuypart2
where getcount1 = 1
Group BY ProductNumber, RelevantYYYYMM, BuyDate, SellDate,
order by ProductNumber
--select * from #firstbuypart3


--Not sure what logic to use to get only one specific row per ProductNumber and RelevantYYYYMM
drop table #firstbuypart4
select b.*, a.BuyDate as BuyDateA, a.SellDate as SellDateA,
CAST(DATEPART(yyyy, a.BuyDateA) AS varchar(4))+ 

CAST(REPLICATE('0', 2-LEN(MONTH(a.BuyDateA))) AS varchar(2)) +

CAST(DATEPART(MONTH, a.BuyDateA) AS varchar(2)) AS RelevantYYYYMM_A  
into #firstdischargepart4
From products a
JOIN #firstbuypart3 b on a.ProductNumber = b.ProductNumber
WHERE 
DATEDIFF(DAY,b.BuyDate,a.BuyDate) BETWEEN 0 AND 30
--select * from #firstbuypart4

【问题讨论】:

  • 你能添加你目前得到的查询吗?
  • 请将您的示例数据添加为“n”粘贴块。
  • 这行得通吗,还是有其他方法可以复制/粘贴示例数据?

标签: sql-server-2008 count group-by partition-by


【解决方案1】:

我认为这可能是您需要的:

注意_1:您不应使用特定于文化的日期时间格式!看这里:https://stackoverflow.com/a/34275965/5089204

注意_2:注意日期时间! BETWEEN @StartDate AND @EndDate 将不包括来自 @EndDate 的 BuyDates,如果有时间的话!

SET LANGUAGE ENGLISH;

DECLARE @tbl TABLE(productNumber INT,Account INT,BuyDate DATETIME,SellDate DATETIME);
INSERT INTO @tbl VALUES
 (27530,8605148,'11/5/15 7:03 PM','11/11/15 2:02 PM')
,(27530,8582063,'10/14/15 7:17 PM','10/16/15 1:48 PM')
,(27530,8566748,'9/30/15 6:51 PM','10/13/15 2:09 PM')
,(101167,8622268,'11/27/15 7:50 PM','11/27/15 8:35 PM')
,(101167,8622267,'11/27/15 8:49 AM','11/27/15 11:02 AM')
,(101167,8622265,'11/26/15 8:47 AM','11/26/15 11:14 AM')
,(101167,8622272,'11/25/15 7:43 PM','11/25/15 8:40 PM')
,(102432,8611407,'11/12/15 5:41 PM','11/25/15 11:50 AM')
,(102432,8600664,'11/2/15 12:04 AM','11/4/15 12:56 PM')
,(102432,8573179,'10/5/15 2:03 PM','10/7/15 3:47 PM')
,(127819,8581822,'10/14/15 7:49 PM','10/18/15 10:46 AM')
,(127819,8572308,'10/2/15 4:38 PM','10/4/15 2:28 PM')
,(127819,8571220,'10/2/15 9:34 AM','10/2/15 4:25 PM')
,(141105,8612529,'11/16/15 7:45 AM','11/16/15 3:06 PM')
,(141105,8612460,'11/14/15 10:04 AM','11/14/15 12:48 PM')
,(141105,8608738,'11/10/15 12:53 PM','11/10/15 3:19 PM')
,(141105,8603007,'11/3/15 5:28 PM','11/9/15 3:54 PM')
,(141105,8595018,'10/28/15 7:23 PM','11/2/15 2:09 PM')
,(141105,8562238,'10/22/15 8:40 AM','10/27/15 5:11 PM');

declare @StartDate as date  = '01/01/2015';
declare @EndDate as date = '12/31/2015';

WITH Extended AS
(
    SELECT ProductNumber
          ,Account
          ,BuyDate
          ,SellDate
          ,CAST(YEAR(BuyDate) AS VARCHAR(4))+ REPLACE(STR(MONTH(BuyDate),2),' ','0') AS RelevantYYYYMM
          ,ROW_NUMBER() OVER(PARTITION BY ProductNumber ORDER BY productNumber,BuyDate DESC) AS RowInx
          ,COUNT(BuyDate) OVER(PARTITION BY ProductNumber) AS CountBuyDates
    FROM @tbl
    WHERE BuyDate BETWEEN @StartDate AND @EndDate --ATTENTION!!! Be aware of DateTime! this will NOT include BuyDates from @EndDate, if there is a time!
)
,DayDiffs AS
(
    SELECT *
          ,DATEDIFF(DAY,ext.BuyDate
                      ,(SELECT innerExt.BuyDate FROM Extended AS innerExt WHERE innerExt.productNumber=ext.productNumber AND innerExt.RowInx=ext.RowInx-1)) AS IntervalRecentToSecond 
          ,DATEDIFF(DAY,(SELECT innerExt.BuyDate FROM Extended AS innerExt WHERE innerExt.productNumber=ext.productNumber AND innerExt.RowInx=ext.RowInx-1)
                       ,(SELECT innerExt.BuyDate FROM Extended AS innerExt WHERE innerExt.productNumber=ext.productNumber AND innerExt.RowInx=ext.RowInx-2)) AS IntervalSecondToThird
    FROM Extended AS ext
    WHERE ext.CountBuyDates>=3 --min three
      AND ext.RowInx=3 --this is the relevant row
)
SELECT productNumber 
      ,Account 
      ,RelevantYYYYMM AS Relev
      ,IntervalRecentToSecond AS Diff1To2
      ,IntervalSecondToThird AS Diff2To3
FROM DayDiffs

结果是:

productNumber   Account Relev   Diff1To2    Diff2To3
27530           8566748 201509  14          22
101167          8622265 201511  1           0
102432          8573179 201510  28          10
127819          8571220 201510  0           12
141105          8608738 201511  4           2

我必须承认,我并没有真正理解您的“RelevantYYYYMM”分组应该如何工作。但我认为,您将能够在自己身上摆弄这个......如果不是:回来做一个简短的解释。

【讨论】:

  • 此查询将在仪表板中使用,该仪表板以 YYYYMM 格式显示今年和去年每月的数据。这是一个更好的例子:一名患者于 2015 年 12 月 22 日住院。医生想知道患者是否在 2015 年 12 月 22 日之前的 30 天内到过医院。患者于 2015 年 12 月 11 日,也就是 30 天内做了。然后医生想知道患者在 2015 年 12 月 11 日之前的 30 天内是否在这里。如果是,患者应该出现在 201512 的仪表板上。该患者不应出现在 201511 的仪表板下,因为在 11 月之前,患者从未来过医院
  • 就我而言,我想知道在 2015 年 12 月 22 日(12 月)购买的产品 (ProductNumber) 是否在 2015 年 12 月 22 日之前的 30 天购买过。假设它也是在 2015 年 12 月 11 日和 2015 年 11 月 28 日购买的。 2015 年 12 月 11 日在 30 天范围内,2015 年 11 月 28 日在 2015 年 12 月 11 日之后的 30 天范围内。因此,对于 12 月,您应该将此产品包含在仪表板计数中。您不会在 11 月包含它,因为当您查看 11 月购买的所有产品时,它无法创建与 12 月相同的场景。
  • 如果我需要更好地解释它,请告诉我。我认为我没有澄清的是我们正在从每个月的角度查看数据。在 12 月查看产品,您会看到我描述的场景是否存在。如果是这样,它将被添加到 12 月的计数中。然后移动到 11 月,看看 11 月是否满足描述中的逻辑……然后是 10 月等等。
  • @NonProgrammer,您好,很抱歉回复晚了,圣诞节,家人,:-) 您的问题很容易解决 AFAIC:将您的@endDate 设置为有问题的月份?或者您添加一个参数@RelevantYYYYMM 并将其放在“DayDiffs”-CTE 的 WHERE 子句中(类似于AND @RelevantYYYYMM<=ext.RelevantYYYYMM)。如果我理解正确:如果您在相关月份的上边界剪切结果集,我给您的逻辑应该返回正确的数据。只需在末尾添加WHERE Diff1To2<=30 AND Diff2To3<=30,这有帮助吗?
  • 进行一些更改并添加一个光标,我就能得到我需要的东西!问题是仪表板应用程序仅采用开始和结束日期参数。所以我必须创建一个游标并分配 YYYYMM 并循环它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-19
  • 2020-06-22
  • 2016-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多