Повторите значение между двумя значениями в столбце
Я пытаюсь повторить первое определенное непустое значение в столбце до следующего определенного непустого значения в том же столбце. Как я могу это сделать?
Данные выглядят так:
ID | Msg
---+-----
1 |
2 |
3 |
4 |
5 | Beg
6 | End
7 |
8 | Beg
9 |
10 |
11 |
12 | End
Это должно быть так:
ID | Msg
---+-----
1 |
2 |
3 |
4 |
5 | Beg
6 | End
7 |
8 | Beg
9 | Beg
10 | Beg
11 | Beg
12 | End
Я смотрел в LAG()
а также LEAD()
но я продолжаю думать, что мне придется использовать CURSOR
для этого. Я просто знаю о них, но еще не использовал их в таком случае.
5 ответов
Просто еще один вариант с использованием флага и sum() over
пример
Select ID
,Msg = case when sum( case when [Msg]='Beg' then 1 when [Msg]='End' then -1 else 0 end ) over (order by ID) = 1 and Msg='' then 'Beg' else Msg end
From YourTable
Возвращает
ID Msg
1
2
3
4
5 Beg
6 End
7
8 Beg
9 Beg
10 Beg
11 Beg
12 End
Поскольку вы используете MSSQL, вы можете написать CTE, чтобы получить результат, который вы ищете.
Попробуйте этот CTE:
declare @tab table
(
id int,
msg char(3)
)
insert into @tab
values
(1, ''),
(2, ''),
(3, ''),
(4, ''),
(5, 'Beg'),
(6, 'End'),
(7, ''),
(8, 'Beg'),
(9, ''),
(10, ''),
(11, ''),
(12, 'End')
;with cte as
(
select top 1 tab.id, tab.msg
from @tab tab
order by tab.id
union all
select tab.id, case when tab.msg = '' and cte.msg = 'beg' then cte.msg else tab.msg end
from @tab tab
inner join cte cte on cte.id + 1 = tab.id
)
select *
from cte
Данные
DECLARE @id AS TABLE (
ID INT
, MSG VARCHAR(3)
)
INSERT INTO @ID (ID, MSG)
SELECT 1, ''
UNION
SELECT 2, ''
UNION
SELECT 3, ''
UNION
SELECT 4, ''
UNION
SELECT 5, 'Beg'
UNION
SELECT 6, 'End'
UNION
SELECT 7, ''
UNION
SELECT 8, 'Beg'
UNION
SELECT 9, ''
UNION
SELECT 10, ''
UNION
SELECT 11, ''
UNION
SELECT 12, 'End'
запрос
SELECT
final.id
, CASE
WHEN msg = '' AND C.begCount>c.EndCount THEN 'Beg'
ELSE final.MSG
END Msg
FROM @id final
INNER JOIN
(
SELECT ID
, (SELECT COUNT(*) FROM @ID B WHERE B.ID < MAIN.ID AND MSG ='BEG') begCount
, (SELECT COUNT(*) FROM @ID B WHERE B.ID < MAIN.ID AND MSG ='END') EndCount
FROM @id MAIN
) C
ON C.ID = final.ID
Вот sql без самостоятельных объединений - используя только оконные функции
select
dat.id,
isnull(nullif(max(dat.msg) over (partition by dat.gr), 'End'), dat.msg) as msg
from (
select
dat.id,
dat.msg,
dat.wind + sum(dat.is_end) over (order by dat.id) as gr
from (
select
t.id,
t.msg,
sum(iif(t.msg = 'Beg' ,1,0)) over (order by t.id) as wind,
iif (t.msg = 'End', 1, 0) as is_end
from t
) dat
) dat
Вы можете попробовать следующий код, он будет работать для случая, который вы запросили, но не уверен, будет ли он работать для всех остальных случаев, поскольку инструкция case специфична для описанного выше позиционирования:
WITH cte AS(
SELECT *, LEAD(Msg, 1, 0) OVER (ORDER BY ID) AS leadval, LAG(Msg, 1, 0) OVER (ORDER BY ID) AS lagval
FROM msg),
cte2 AS(
SELECT cte.ID,
cte.Msg,
cte.leadval,
cte.lagval,
CASE WHEN cte.Msg = 'Beg' THEN 'Beg'
WHEN cte.Msg = '' AND cte.leadval = '' AND cte.lagval = 'Beg' THEN 'Beg'
WHEN cte.Msg = '' AND cte.leadval = 'END' THEN 'Beg'
ELSE cte.Msg end AS Msg2
FROM cte), cte3 AS(
SELECT *, LEAD(cte2.Msg2, 1, 0) OVER (ORDER BY cte2.ID) AS 'LeadVal2'
FROM cte2)
SELECT ID, CASE WHEN cte3.Msg2 = '' AND cte3.LeadVal2 = 'Beg' AND cte3.leadval <> 'Beg' THEN 'Beg' ELSE cte3.Msg2 END AS msg
FROM cte3
Выход: