【发布时间】:2021-08-16 08:28:22
【问题描述】:
我在下面有一张桌子。
我正在寻找连续 12 个月的交易(以黄色、橙色和绿色突出显示)。对于绿色,有 06/2013 月份的 2 笔交易,但它补偿了 07/2013 的交易,因为它丢失了。也有一些交易在一个月内有超过 2 笔交易,但可以对其他缺失的交易进行补偿。
下面是构造表的查询。
CREATE TABLE #Temp
(
ProductProvider VARCHAR(200),
ProductType VARCHAR(200),
PolicyID BIGINT,
TransactionDate DATE,
TotalRevenue DECIMAL(10,2)
)
--DROP TABLE #temp
insert into #Temp
values
('XYZ', 'ONG', 2789753, '2009-04-30', 0.19)
,('XYZ', 'ONG', 2789753, '2009-05-31', 0.19)
,('XYZ', 'ONG', 2789753, '2009-07-31', 0.19)
,('XYZ', 'ONG', 2789753, '2009-08-31', 0.19)
,('XYZ', 'ONG', 2789753, '2009-10-31', 0.19)
,('XYZ', 'ONG', 2789753, '2009-11-30', 0.19)
,('XYZ', 'ONG', 2789753, '2009-12-31', 0.19)
,('XYZ', 'ONG', 2789753, '2010-01-31', 0.19)
,('XYZ', 'ONG', 2789753, '2010-02-28', 0.19)
,('XYZ', 'ONG', 2789753, '2010-03-31', 0.19)
,('XYZ', 'ONG', 2789753, '2010-04-30', 0.19)
,('XYZ', 'ONG', 2789753, '2010-05-31', 0.19)
,('XYZ', 'ONG', 2789753, '2010-06-30', 0.19)
,('XYZ', 'ONG', 2789753, '2010-07-31', 0.19)
,('XYZ', 'ONG', 2789753, '2010-08-31', 0.19)
,('XYZ', 'ONG', 2789753, '2010-09-30', 0.19)
,('XYZ', 'ONG', 2789753, '2010-10-30', 0.19)
,('XYZ', 'ONG', 2789753, '2011-01-31', 0.19)
,('XYZ', 'ONG', 2789753, '2011-02-28', 0.19)
,('XYZ', 'ONG', 2789753, '2011-04-30', 0.19)
,('XYZ', 'ONG', 2789753, '2011-05-31', 0.19)
,('XYZ', 'ONG', 2789753, '2011-06-30', 0.19)
,('XYZ', 'ONG', 2789753, '2011-07-31', 0.19)
,('XYZ', 'ONG', 2789753, '2011-08-31', 0.19)
,('XYZ', 'ONG', 2789753, '2011-10-31', 0.19)
,('XYZ', 'ONG', 2789753, '2011-12-31', 0.19)
,('XYZ', 'ONG', 2789753, '2012-05-31', 0.19)
,('XYZ', 'ONG', 2789753, '2012-06-30', 0.19)
,('XYZ', 'ONG', 2789753, '2012-07-31', 0.19)
,('XYZ', 'ONG', 2789753, '2012-08-31', 0.19)
,('XYZ', 'ONG', 2789753, '2012-09-30', 0.19)
,('XYZ', 'ONG', 2789753, '2012-10-31', 0.19)
,('XYZ', 'ONG', 2789753, '2012-11-30', 0.19)
,('XYZ', 'ONG', 2789753, '2012-12-31', 0.19)
,('XYZ', 'ONG', 2789753, '2013-01-31', 0.19)
,('XYZ', 'ONG', 2789753, '2013-02-28', 0.19)
,('XYZ', 'ONG', 2789753, '2013-03-31', 0.19)
,('XYZ', 'ONG', 2789753, '2013-04-30', 0.19)
,('XYZ', 'ONG', 2789753, '2013-05-31', 0.19)
,('XYZ', 'ONG', 2789753, '2013-06-25', 0.19)
,('XYZ', 'ONG', 2789753, '2013-06-30', 0.19)
,('XYZ', 'ONG', 2789753, '2013-08-31', 0.19)
,('XYZ', 'ONG', 2789753, '2013-09-30', 0.19)
,('XYZ', 'ONG', 2789753, '2013-10-31', 0.19)
,('XYZ', 'ONG', 2789753, '2013-11-30', 0.19)
,('XYZ', 'ONG', 2789753, '2013-12-31', 0.19)
,('XYZ', 'ONG', 2789753, '2014-01-31', 0.19)
,('XYZ', 'ONG', 2789753, '2014-02-28', 0.19)
,('XYZ', 'ONG', 2789753, '2014-03-31', 0.19)
,('XYZ', 'ONG', 2789753, '2014-04-30', 0.19)
,('XYZ', 'ONG', 2789753, '2014-08-30', 0.19)
下面是我的第一次尝试。
select *
from (select t.*,
count(*) over (partition by ProductProvider, ProductType, TotalRevenue, DATEADD(m,seqnum, convert(date, DATEADD(month, DATEDIFF(month, 0, TransactionDate), 0)))) as cnt_group
from (select *,
row_number() over (partition by ProductProvider, ProductType, TotalRevenue order by convert(date, DATEADD(month, DATEDIFF(month, 0, TransactionDate), 0))) as seqnum
from #Temp
) t
) t
where cnt_group = 12
order by transactiondate
我从上述查询中一无所获(我想我无法对 ProductProvider/ProductType/PolicyId 进行行编号,因为交易日期都不同,因此给了我第 1 到 51 行没有分组)。
下面是我的第二次尝试。
SELECT DISTINCT *,
month1+month2+month3+month4+month5+month6+month7+month8+month9+month10+month11+month12 AS Total
FROM
(
SELECT a.*,
1 AS Month1,
CASE WHEN b.ProductProvider IS NULL THEN 0 ELSE 1 END AS Month2,
CASE WHEN c.ProductProvider IS NULL THEN 0 ELSE 1 END AS Month3,
CASE WHEN d.ProductProvider IS NULL THEN 0 ELSE 1 END AS Month4,
CASE WHEN e.ProductProvider IS NULL THEN 0 ELSE 1 END AS Month5,
CASE WHEN f.ProductProvider IS NULL THEN 0 ELSE 1 END AS Month6,
CASE WHEN g.ProductProvider IS NULL THEN 0 ELSE 1 END AS Month7,
CASE WHEN h.ProductProvider IS NULL THEN 0 ELSE 1 END AS Month8,
CASE WHEN i.ProductProvider IS NULL THEN 0 ELSE 1 END AS Month9,
CASE WHEN j.ProductProvider IS NULL THEN 0 ELSE 1 END AS Month10,
CASE WHEN k.ProductProvider IS NULL THEN 0 ELSE 1 END AS Month11,
CASE WHEN l.ProductProvider IS NULL THEN 0 ELSE 1 END AS Month12
FROM #Temp a
LEFT JOIN #temp b ON a.ProductProvider = b.ProductProvider
AND a.ProductType = b.ProductType
AND a.policyid = b.policyid
AND convert(date, DATEADD(month, DATEDIFF(month, 0, b.TransactionDate), 0)) = DATEADD(m, 1, convert(date, DATEADD(month, DATEDIFF(month, 0, a.TransactionDate), 0)))
LEFT JOIN #temp c ON a.ProductProvider = c.ProductProvider
AND a.ProductType = c.ProductType
AND a.policyid = c.policyid
AND convert(date, DATEADD(month, DATEDIFF(month, 0, c.TransactionDate), 0)) = DATEADD(m, 2, convert(date, DATEADD(month, DATEDIFF(month, 0, a.TransactionDate), 0)))
LEFT JOIN #temp d ON a.ProductProvider = d.ProductProvider
AND a.ProductType = d.ProductType
AND a.policyid = d.policyid
AND convert(date, DATEADD(month, DATEDIFF(month, 0, d.TransactionDate), 0)) = DATEADD(m, 3, convert(date, DATEADD(month, DATEDIFF(month, 0, a.TransactionDate), 0)))
LEFT JOIN #temp e ON a.ProductProvider = e.ProductProvider
AND a.ProductType = e.ProductType
AND a.policyid = e.policyid
AND convert(date, DATEADD(month, DATEDIFF(month, 0, e.TransactionDate), 0)) = DATEADD(m, 4, convert(date, DATEADD(month, DATEDIFF(month, 0, a.TransactionDate), 0)))
LEFT JOIN #temp f ON a.ProductProvider = f.ProductProvider
AND a.ProductType = f.ProductType
AND a.policyid = f.policyid
AND convert(date, DATEADD(month, DATEDIFF(month, 0, f.TransactionDate), 0)) = DATEADD(m, 5, convert(date, DATEADD(month, DATEDIFF(month, 0, a.TransactionDate), 0)))
LEFT JOIN #temp g ON a.ProductProvider = g.ProductProvider
AND a.ProductType = g.ProductType
AND a.policyid = g.policyid
AND convert(date, DATEADD(month, DATEDIFF(month, 0, g.TransactionDate), 0)) = DATEADD(m, 6, convert(date, DATEADD(month, DATEDIFF(month, 0, a.TransactionDate), 0)))
LEFT JOIN #temp h ON a.ProductProvider = h.ProductProvider
AND a.ProductType = h.ProductType
AND a.policyid = h.policyid
AND convert(date, DATEADD(month, DATEDIFF(month, 0, h.TransactionDate), 0)) = DATEADD(m, 7, convert(date, DATEADD(month, DATEDIFF(month, 0, a.TransactionDate), 0)))
LEFT JOIN #temp i ON a.ProductProvider = i.ProductProvider
AND a.ProductType = i.ProductType
AND a.policyid = i.policyid
AND convert(date, DATEADD(month, DATEDIFF(month, 0, i.TransactionDate), 0)) = DATEADD(m, 8, convert(date, DATEADD(month, DATEDIFF(month, 0, a.TransactionDate), 0)))
LEFT JOIN #temp j ON a.ProductProvider = j.ProductProvider
AND a.ProductType = j.ProductType
AND a.policyid = j.policyid
AND convert(date, DATEADD(month, DATEDIFF(month, 0, j.TransactionDate), 0)) = DATEADD(m, 9, convert(date, DATEADD(month, DATEDIFF(month, 0, a.TransactionDate), 0)))
LEFT JOIN #temp k ON a.ProductProvider = k.ProductProvider
AND a.ProductType = k.ProductType
AND a.policyid = k.policyid
AND convert(date, DATEADD(month, DATEDIFF(month, 0, k.TransactionDate), 0)) = DATEADD(m, 10, convert(date, DATEADD(month, DATEDIFF(month, 0, a.TransactionDate), 0)))
LEFT JOIN #temp l ON a.ProductProvider = l.ProductProvider
AND a.ProductType = l.ProductType
AND a.policyid = l.policyid
AND convert(date, DATEADD(month, DATEDIFF(month, 0, l.TransactionDate), 0)) = DATEADD(m, 11, convert(date, DATEADD(month, DATEDIFF(month, 0, a.TransactionDate), 0)))
) x
我的逻辑是,如果我可以得到 12 列的总数,这就是我将保留的结果。然而,使用这种方法,它似乎每行计算 12 个连续事务(我猜该术语连续运行 12 个月)。因此,连续 12 个月检查第 1 行,连续 12 个月检查第 2 行,连续 12 个月检查第 3 行,等等。最后没有明确的指示符,我可以从 #temp 表中选择和删除哪些连续 12 个月所以他们不会被重复计算。此外,如果同一年有超过 1 笔交易可以补偿其他缺失的交易,则此方法也不起作用,因为它总共只计算 11 笔。
最后一种方法是使用循环。所以选择第1行,检查是否有12个连续交易,如果没有选择第2行,如果没有选择第3行,如果是,则选择第3行的日期到第3行的最后12个日期并将其放在某处并删除它们来自表#temp。然后再次开始循环。但是,由于除了 DO-WHILE-PRINT 之类的简单循环之外,我在构造循环方面存在局限性,因此我无法为此编写任何查询。
我使用的是 Microsoft SQL 2012。
17/08/2021 - 我在下面还有另一种方法。
;WITH CTEDATES
AS
(
SELECT ROW_NUMBER() OVER (ORDER BY TransactionDate asc ) AS ROWNUMBER,* FROM #Temp
),
CTEDATES1
AS
(
SELECT *, 1 as groupid FROM CTEDATES WHERE ROWNUMBER=1
UNION ALL
SELECT a.*,case datediff(m, b.transactiondate,a.transactiondate) when 1 then b.groupid else b.groupid+1 end as gap FROM CTEDATES A INNER JOIN CTEDATES1 B ON A.ROWNUMBER-1 = B.ROWNUMBER
)
, cte AS
(
select productprovider, producttype, policyid, totalrevenue, MIN(transactiondate) as startdate, max(transactiondate) as enddate from CTEDATES1 group by productprovider, producttype, policyid, totalrevenue,groupid
)
SELECT b.productprovider,
b.producttype,
b.policyid,
b.totalrevenue,a.startdate, a.enddate, COUNT(b.totalrevenue) AS TotalTransaction
FROM cte a
INNER JOIN #temp b ON a.productprovider = b.ProductProvider
AND a.producttype = b.ProductType
AND a.policyid = b.PolicyID
AND a.totalrevenue = b.totalrevenue
WHERE b.TransactionDate BETWEEN a.startdate AND a.enddate
GROUP BY b.productprovider,
b.producttype,
b.policyid,
b.totalrevenue,
a.startdate,
a.enddate
它可以检测间隙和这些间隙之间的总交易量,最后我可以选择 TotalTransactions >= 12 并应用另一个 row_numbering 以获得恰好 12 个连续交易。然而,使用这种方法,不幸的是,它无法检测到更可能用于补偿缺失的月份年份的同一月份年份内的多个交易。下面是结果。
第 8 列的结果应该是 StartDate = 2012/05/31 和 EndDate = 2014/04/30 且 TotalTransaction = 24,因为在 06/2013 年有 2 笔交易,其中一笔是为了补偿缺少 07/2013 月份的交易。
18/08/2021 - 下面的另一种方法仅适用于这种情况。
IF OBJECT_ID('tempdb..#AssigningNewTransactionDate') IS NOT NULL
DROP TABLE #AssigningNewTransactionDate
;WITH cte AS
(
SELECT *,
CONVERT(date, DATEADD(month, DATEDIFF(month, 0, TransactionDate), 0)) AS FirstoftheMonth
FROM #temp
)
,cte2 AS
(
SELECT productprovider, producttype, policyid, totalrevenue, firstofthemonth, COUNT(firstofthemonth) AS NumberofFirstOfTheMonth
FROM cte
GROUP BY productprovider, producttype, policyid, totalrevenue, firstofthemonth
)
, cte3 AS
(
SELECT a.*,
b.NumberofFirstOfTheMonth,
ROW_NUMBER() OVER(PARTITION BY a.productprovider, a.producttype, a.policyid, a.totalrevenue, a.firstofthemonth ORDER by a.firstofthemonth) AS Row_num
FROM cte a
LEFT JOIN cte2 b ON a.productprovider = b.ProductProvider
AND a.producttype = b.ProductType
AND a.policyid = b.PolicyID
AND a.totalrevenue = b.TotalRevenue
AND a.firstofthemonth = b.firstofthemonth
)
SELECT *,
CASE WHEN NumberofFirstOfTheMonth = 2 AND row_num = 1 AND LAG(firstofthemonth, 1) OVER (PARTITION BY productprovider, producttype, policyid, totalrevenue ORDER BY firstofthemonth) = DATEADD(m, -1, firstofthemonth) THEN cte3.TransactionDate
WHEN NumberofFirstOfTheMonth = 2 AND row_num = 2 AND LAG(firstofthemonth, 1) OVER (PARTITION BY productprovider, producttype, policyid, totalrevenue ORDER BY firstofthemonth) = firstofthemonth THEN DATEADD(m, 1, transactiondate)
ELSE cte3.TransactionDate
END AS NewTransactionDate
INTO #AssigningNewTransactionDate
FROM cte3
;WITH CTEDATES
AS
(
SELECT ROW_NUMBER() OVER (ORDER BY NewTransactionDate asc ) AS ROWNUMBER,*
FROM #AssigningNewTransactionDate
)
, CTEDATES1
AS
(
SELECT *, 1 as groupid
FROM CTEDATES WHERE ROWNUMBER=1
UNION ALL
SELECT a.*,case datediff(m, b.newtransactiondate,a.newtransactiondate) when 1 then b.groupid else b.groupid+1 end as gap FROM CTEDATES A INNER JOIN CTEDATES1 B ON A.ROWNUMBER-1 = B.ROWNUMBER
)
, cte AS
(
select productprovider, producttype, policyid, totalrevenue, MIN(newtransactiondate) as startdate, max(newtransactiondate) as enddate
FROM CTEDATES1
GROUP by productprovider, producttype, policyid, totalrevenue,groupid
)
SELECT b.productprovider,
b.producttype,
b.policyid,
b.totalrevenue,a.startdate, a.enddate, COUNT(b.totalrevenue) AS TotalTransaction
FROM cte a
INNER JOIN #AssigningNewTransactionDate b ON a.productprovider = b.ProductProvider
AND a.producttype = b.ProductType
AND a.policyid = b.PolicyID
AND a.totalrevenue = b.totalrevenue
WHERE b.NewTransactionDate BETWEEN a.startdate AND a.enddate
GROUP BY b.productprovider,
b.producttype,
b.policyid,
b.totalrevenue,
a.startdate,
a.enddate
使用此方法,07/13 ...05/13, 06/13, 06/13, 08/13 之间的缺失交易可以通过 06/13 的交易进行补偿。但是,如果发生其他有多个事务的情况,例如 ...04、06、06、07、08... 或 ... 04、07、07、07、08...,则此方法不是万无一失的。
【问题讨论】:
-
补偿的逻辑不是很清楚。例如月份是
..05, 07,07, 09..期望的结果是什么,..05,06,07即补偿 06 或07,08,09..即补偿 08? -
嗨,Serg,对于补偿逻辑,它必须补偿最早的月份(因为所有收入都相同),例如如果月份是 ...05、07、07、09.. . 那么07月份的其中一个需要补偿缺少的06。所以它将是...... 05、06、07、09......谢谢。
-
...05, 07, 07, 07, 09...是否同时补偿 06 和 08?.. 04, 06, 07, 07, 08..呢,05 有补偿吗?补偿规则应在问题中详细说明。 -
嗨,Serg,对于 ..05、07、07、07、09.. 是的,它补偿 06 和 08。对于 .. 04、06、07、07、08.. 是的,它补偿05. 所以同年同月的多次交易必须补缺月的第一个月。如果是.. 05, 07, 07, 09.. 它只会补偿 06 和 08 将继续拖欠付款。我已经更新了逻辑并将其发布在上面。使用 LAG,我可以创建 CASE 逻辑,但它仅适用于 ..05,06,06,08 等场景。是否可以使用 CASE 创建“场景分组”(如场景 ..05、06、06 , 08.. 这样做,场景 ..04,06, 07,07,08.. 这样做等等)?
标签: sql sql-server transactions row-number