在列中的两个值之间重复值

Posted

技术标签:

【中文标题】在列中的两个值之间重复值【英文标题】:Repeat value between two values in a column 【发布时间】:2019-01-14 20:47:22 【问题描述】:

我正在尝试重复列中的第一个特定非空值,直到同一列中的下一个特定非空值。我该怎么做?

数据如下:

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。我只知道这些,但还没有在这种情况下使用它们。

【问题讨论】:

LEAD 可以正常工作。如果 val 是 Beg,则提前 1 个值,如果该值为空白,则用 Beg 填写,否则使用那里的值。 深入阅读:red-gate.com/simple-talk/sql/t-sql-programming/… 这些空白是NULLs 还是只是'' 空字符串? @Sami 空字符串 【参考方案1】:

使用标志和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

【讨论】:

这里最简单的答案。 谢谢!为我工作。 @SQLserving 乐于助人【参考方案2】:

由于您使用的是 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

【讨论】:

当然,在 SSMS 2016 上。 我会更喜欢你的解决方案而不是我的解决方案。谢谢,继续努力!【参考方案3】:

数据

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

【讨论】:

【参考方案4】:

这是没有自连接的 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

【讨论】:

【参考方案5】:

您可以尝试以下代码,它适用于您询问的情况,但不确定它是否适用于所有其他情况,因为 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

输出:

【讨论】:

以上是关于在列中的两个值之间重复值的主要内容,如果未能解决你的问题,请参考以下文章

如何使用flexbox进行两列布局,并在列中的项目之间使用相同的间距? [重复]

将行与标题进行比较,然后在列中插入值并在 VBA 中进行重复检查

MySQL:如何在列中选择具有某些值的 ID [重复]

如果在列中找到重复的单元格值,则返回值

在列中查找具有重复值的行

重复计算两个值之间的距离 Excel