我在SQL Server中遇到了问题。
“Whate'er的设想得到了明确的说明,并且说它流畅的话语”,Nicolas Boileau-Despreaux
好吧,我不认为我能说清楚,但我会试试!而且我想为我糟糕的英语道歉!
我有这张桌子:
id ind lvl result date
1 1 a 3 2017-01-31
2 1 a 3 2017-02-28
3 1 a 1 2017-03-31
4 1 a 1 2017-04-30
5 1 a 1 2017-05-31
6 1 b 1 2017-01-31
7 1 b 3 2017-02-28
8 1 b 3 2017-03-31
9 1 b 1 2017-04-30
10 1 b 1 2017-05-31
11 2 a 3 2017-01-31
12 2 a 1 2017-02-28
13 2 a 3 2017-03-31
14 2 a 1 2017-04-30
15 2 a 3 2017-05-31
如果结果不是1,我想计算组合{ind,lvl}保留在结果1中的月份数,然后重新初始化月数为0。
显然,我需要得到类似的东西:
id ind lvl result date BadResultRemainsFor%Months
1 1 a 3 2017-01-31 0
2 1 a 3 2017-02-28 0
3 1 a 1 2017-03-31 1
4 1 a 1 2017-04-30 2
5 1 a 1 2017-05-31 3
6 1 b 1 2017-01-31 1
7 1 b 3 2017-02-28 0
8 1 b 3 2017-03-31 0
9 1 b 1 2017-04-30 1
10 1 b 1 2017-05-31 2
11 2 a 3 2017-01-31 0
12 2 a 1 2017-02-28 1
13 2 a 3 2017-03-31 0
14 2 a 1 2017-04-30 1
15 2 a 3 2017-05-31 0
因此,如果我在查找 1 的结果为 1个且ID 1 >和lvl a ,我知道已经3个月了。
答案 0 :(得分:2)
通过略微调整输入数据并略微调整我们定义需求的方式,生成预期结果变得非常简单。
首先,我们调整您的date
值,以便唯一不同的是月份和年份 - 日期都是相同的。我已经选择这样做,我为每个值 1 添加1天。事实上,这会产生一个月前进的结果并不重要,因为所有的数值都是相似的变换,所以每月的关系保持不变。
然后,我们介绍一个数字表 - 在这里,我假设一个小的固定表是足够的。如果它不符合您的需求,您可以轻松地在线查找示例,以创建可用于此查询的大型固定数字表。
最后,我们重新制作了问题陈述。我们不是试图计算几个月,而是询问"什么是最小的月数,大于等于零,我需要从当前行返回,找到一个非行的行1结果?"。因此,我们生成此查询:
declare @t table (id int not null,ind int not null,lvl varchar(13) not null,
result int not null,date date not null)
insert into @t(id,ind,lvl,result,date) values
(1 ,1,'a',3,'20170131'), (2 ,1,'a',3,'20170228'), (3 ,1,'a',1,'20170331'),
(4 ,1,'a',1,'20170430'), (5 ,1,'a',1,'20170531'), (6 ,1,'b',1,'20170131'),
(7 ,1,'b',3,'20170228'), (8 ,1,'b',3,'20170331'), (9 ,1,'b',1,'20170430'),
(10,1,'b',1,'20170531'), (11,2,'a',3,'20170131'), (12,2,'a',1,'20170228'),
(13,2,'a',3,'20170331'), (14,2,'a',1,'20170430'), (15,2,'a',3,'20170531')
;With Tweaked as (
select
*,
DATEADD(day,1,date) as dp1d
from
@t
), Numbers(n) as (
select 0 union all select 1 union all select 2 union all select 3 union all select 4
union all
select 5 union all select 6 union all select 7 union all select 8 union all select 9
)
select
id, ind, lvl, result, date,
COALESCE(
(select MIN(n) from Numbers n1
inner join Tweaked t2
on
t2.ind = t1.ind and
t2.lvl = t1.lvl and
t2.dp1d = DATEADD(month,-n,t1.dp1d)
where
t2.result != 1
),
1) as [BadResultRemainsFor%Months]
from
Tweaked t1
COALESCE
只是处理边缘情况,例如您的1,b
数据,其中没有前一行有非1结果。
结果:
id ind lvl result date BadResultRemainsFor%Months
----------- ----------- ------------- ----------- ---------- --------------------------
1 1 a 3 2017-01-31 0
2 1 a 3 2017-02-28 0
3 1 a 1 2017-03-31 1
4 1 a 1 2017-04-30 2
5 1 a 1 2017-05-31 3
6 1 b 1 2017-01-31 1
7 1 b 3 2017-02-28 0
8 1 b 3 2017-03-31 0
9 1 b 1 2017-04-30 1
10 1 b 1 2017-05-31 2
11 2 a 3 2017-01-31 0
12 2 a 1 2017-02-28 1
13 2 a 3 2017-03-31 0
14 2 a 1 2017-04-30 1
15 2 a 3 2017-05-31 0
1 执行调整的另一种方法是使用DATEADD
/ DATEDIFF
对执行" floor"对日期的操作:
DATEADD(month,DATEDIFF(month,0,date),0) as dp1d
将所有日期值重置为本月的第一个而不是下个月。这可能会更多地自然而且#34;对您而言,或者您可能已经在原始数据中提供了此类值。
答案 1 :(得分:2)
假设所有日期都是月末:
;WITH tb(id,ind,lvl,result,date) AS(
select 1,1,'a',3,'2017-01-31' UNION
select 2,1,'a',3,'2017-02-28' UNION
select 3,1,'a',1,'2017-03-31' UNION
select 4,1,'a',1,'2017-04-30' UNION
select 5,1,'a',1,'2017-05-31' UNION
select 6,1,'b',1,'2017-01-31' UNION
select 7,1,'b',3,'2017-02-28' UNION
select 8,1,'b',3,'2017-03-31' UNION
select 9,1,'b',1,'2017-04-30' UNION
select 10,1,'b',1,'2017-05-31' UNION
select 11,2,'a',3,'2017-01-31' UNION
select 12,2,'a',1,'2017-02-28' UNION
select 13,2,'a',3,'2017-03-31' UNION
select 14,2,'a',1,'2017-04-30' UNION
select 15,2,'a',3,'2017-05-31'
)
SELECT t.id,t.ind,t.lvl,t.result,t.date
,CASE WHEN t.isMatched=1 THEN ROW_NUMBER()OVER(PARTITION BY t.ind,t.lvl,t.id-t.rn ORDER BY t.id) ELSE 0 END
FROM (
SELECT t1.*,c.MonthDiff,CASE WHEN c.MonthDiff=t1.result THEN 1 ELSE 0 END AS isMatched
,CASE WHEN c.MonthDiff=t1.result THEN ROW_NUMBER()OVER(PARTITION BY t1.ind,t1.lvl,CASE WHEN c.MonthDiff=t1.result THEN 1 ELSE 0 END ORDER BY t1.id) ELSE null END AS rn
FROM tb AS t1
LEFT JOIN tb AS t2 ON t1.ind=t2.ind AND t1.lvl=t2.lvl AND t2.id=t1.id-1
CROSS APPLY(VALUES(ISNULL(DATEDIFF(MONTH,t2.date,t1.date),1))) c(MonthDiff)
) AS t
ORDER BY t.id
id ind lvl result date ----------- ----------- ---- ----------- ---------- -------------------- 1 1 a 3 2017-01-31 0 2 1 a 3 2017-02-28 0 3 1 a 1 2017-03-31 1 4 1 a 1 2017-04-30 2 5 1 a 1 2017-05-31 3 6 1 b 1 2017-01-31 1 7 1 b 3 2017-02-28 0 8 1 b 3 2017-03-31 0 9 1 b 1 2017-04-30 1 10 1 b 1 2017-05-31 2 11 2 a 3 2017-01-31 0 12 2 a 1 2017-02-28 1 13 2 a 3 2017-03-31 0 14 2 a 1 2017-04-30 1 15 2 a 3 2017-05-31 0
答案 2 :(得分:1)
假设日期在月份中不断增加,您可以使用如下窗口函数:
select
t.id, ind, lvl, result, dat,
case when result = 1 then row_number() over (partition by grp order by id) else 0 end x
from (
select t.*,
dense_rank() over (order by e, result) grp
from (
select
t.*,
row_number() over (order by id) - row_number() over (partition by ind, lvl, result order by id) e
from your_table t
order by id) t ) t;