Durch leichtes Anpassen der Eingabedaten und durch geringfügiges Optimieren der Definition der Anforderung wird es recht einfach, die erwarteten Ergebnisse zu erzielen.
Zuerst zwicken wir Ihre date
Werte, so dass das einzige, was variiert, der Monat und das Jahr ist - die Tage sind alle gleich. Ich habe gewählt, dass das Hinzufügen von 1 Tag zu jedem Wert . Die Tatsache, dass dies zu Ergebnissen führt, die um einen Monat fortgeschritten sind, spielt hier keine Rolle, da alle Werte in ähnlicher Weise transformiert werden und somit die monatlichen Beziehungen gleich bleiben.
Dann führen wir eine Zahlentabelle ein - hier habe ich angenommen, dass ein kleiner fester Tisch angemessen ist. Wenn es Ihren Anforderungen nicht entspricht, können Sie online problemlos Beispiele finden, um eine große Tabelle mit festen Zahlen zu erstellen, die Sie für diese Abfrage verwenden können.
Und schließlich, wir überarbeiten die Problemstellung. Anstatt zu versuchen, Monate zu zählen, fragen wir stattdessen "Was ist die kleinste Anzahl von Monaten, größer gleich null, dass ich von der aktuellen Zeile zurückgehen muss, um eine Zeile mit einem Nicht-1-Ergebnis zu finden?". Und so produzieren wir diese Anfrage:
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
Die COALESCE
nur gibt es mit dem Rand Fall zu behandeln, wie für Ihre 1,b
Daten, in denen es keine vorherige Zeile mit einem nicht-1 Ergebnis.
Ergebnisse:
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
Ein alternativer Weg, um die Anpassung durchzuführen ist, eine einen "Boden" Operation gegen die Tage DATEADD
/DATEDIFF
Paar zu verwenden, um auszuführen:
DATEADD(month,DATEDIFF(month,0,date),0) as dp1d
Bei dieser Einstellung werden alle Datumswerte als erste des jeweiligen Monats und nicht als Folgemonat zurückgesetzt.Dies könnte für Sie "natürlicher" sein, oder Sie haben möglicherweise bereits solche Werte in Ihren Originaldaten verfügbar.
Sind wir garantiert, dass alle "Datum" -Werte nur Daten sind und immer für den letzten Tag eines jeden Monats? –
@Damien_The_Unbeliever Ja, es wird immer der letzte Tag eines jeden Monats sein! – Flesym