【问题标题】:SQL - CASE WHEN within STUFF function for conditional delimiterSQL - 条件分隔符的 STUFF 函数中的 CASE WHEN
【发布时间】:2018-02-09 15:35:14
【问题描述】:

长话短说:我试图在我的 STUFF() 函数中使用条件分隔符。出于我的数据目的,以下示例中的值是基于前两位数的系列。

| uniqueID   | value    |
| name1      | 110      |
| name1      | 111      |
| name1      | 112      |
| name1      | 113      |
| name1      | 120      |
| name1      | 121      |
| name1      | 130      |
| name1      | 131      |

一个 STUFF() 函数如下所示:

select t.uniqueid, stuff((select distinct ',' + v.value
    from #temp v
    where v.uniqueID = t.uniqueid
    for XML path ('')),1,1,'')
from #temp t

...返回:

| uniqueID   | value                                  |
| name1      | 110, 111, 112, 113, 120, 121, 130, 131 |

再次,由于我们在内部根据前两位数对上述代码进行分类,我希望在同一系列中的值之间使用逗号,并在一个系列中的最后一个值,下一个系列中的第一个值。

理想输出:

| uniqueID   | value                                |
| name1      | 110, 111, 112, 113@120, 121@130, 131 |

我的第一个想法是按原样运行 STUFF(),然后在结果字符串中搜索,但不确定这是否聪明,甚至不知道该怎么做。

我的第二个想法是可以使用 CASE WHEN 语句代替 STUFF() 中的分隔符 ',' 声明,但我不知道如何将一个值与 STUFF() 中的下一个值进行比较.

我最后的想法可能是在值的 STUFF() 之前和“系列”结束的地方进行比较,只需添加 '+ @',这将在 STUFF() 期间被拉入。

任何帮助或创造性的方法将不胜感激。提前致谢。

【问题讨论】:

    标签: sql function tsql rows string-comparison


    【解决方案1】:

    你应该可以使用lag()

    select t.uniqueid,
           stuff( (select distinct
                          (case when left(prev_value, 2) = left(value, 2)
                                then ','
                                else '@'
                           end) + v.value
                   from (select v.*, lag(v.value) over (partition by uniqueid order by v.value) as prev_value
                         from #temp v
                        ) v
                   where v.uniqueID = t.uniqueid
                   order by v.value
                   for XML path ('')
                  ), 1, 1, '')
    from #temp t
    

    【讨论】:

    • 您好,感谢您的回复。我尝试了您的解决方案,但当使用多个 uniqueID 或值发生乱序时,它不起作用。
    • drop table #temp create table #temp (uniqueID varchar(100),value varchar(100)) insert into #temp values ('name1','110') insert into #temp values ('name1','150') insert into #temp values ('name1','121') insert into #temp values ('name2','110') insert into #temp values ('name2','140') insert into #temp values ('name3','110') insert into #temp values ('name3','150')
    • select t.uniqueid, stuff( (select (case when left(prev_value, 2) = left(value, 2) then ',' else '@' end) + v.value from (select v.*, lag(v.value) over (order by v.value) as prev_value from #temp v ) v where v.uniqueID = t.uniqueid order by v.value for XML path ('') ), 1, 1, '') as [stuffstring] from #temp t
    • @JustinFreer 。 . .无论值的顺序如何,这都应该特别有效,因为它有一个order by 子句。但是,查询缺少唯一 ID 的 partition by
    • 你是对的。我忘记了在一些调整期间我已经接受了订单。道歉。
    【解决方案2】:

    它不漂亮,但我认为这可行:

    -- Set up temp table and test data
    create table #values
    (
        uniqueID varchar(100),
        value int
    )
    
    insert into #values
    select 'name1', 110
    union
    select 'name1', 111
    union
    select 'name1', 112
    union
    select 'name1', 113
    union
    select 'name1', 120
    union
    select 'name1', 121
    union
    select 'name1', 130
    union
    select 'name1', 131
    union
    select 'name2', 110
    union
    select 'name2', 111
    union
    select 'name2', 112
    union
    select 'name2', 113
    union
    select 'name2', 114
    union
    select 'name2', 120
    union
    select 'name2', 130
    union
    select 'name2', 131
    union
    select 'name2', 132
    
    go
    
    -- Create CTE to add '@' to the last value in each series
    with results (uniqueId, [value])
    as
    (
        select distinct
                v1.uniqueID
            ,case when v2.[value] is null then convert(varchar,v1.[value]) + '@' else convert(varchar,v1.[value]) end as [value]
        from #values v1
        left join #values v2 on v1.uniqueID = v2.uniqueID and v2.[value] > v1.[value] and v1.[value] / 10 = v2.[value] / 10
    )
    
    -- Return STUFFed final string (using reverse to remove trailing '@' without repeating code)
    select 
         uniqueId
        ,reverse(stuff(reverse(replace(stuff((select distinct ',' + [value] from results r2 where r1.uniqueId = r2.uniqueId for xml path ('')),1,1,''),'@,','@')),1,1,'')) as [value]
    from results r1
    
    drop table #values
    

    结果:

    /-----------------------------------------------\
    |uniqueId | value                               |
    |---------|-------------------------------------|
    | name1   | 110,111,112,113@120,121@130,131     |
    | name1   | 110,111,112,113@120,121@130,131     |
    | name1   | 110,111,112,113@120,121@130,131     |
    | name1   | 110,111,112,113@120,121@130,131     |
    | name1   | 110,111,112,113@120,121@130,131     |
    | name1   | 110,111,112,113@120,121@130,131     |
    | name1   | 110,111,112,113@120,121@130,131     |
    | name1   | 110,111,112,113@120,121@130,131     |
    | name2   | 110,111,112,113,114@120@130,131,132 |
    | name2   | 110,111,112,113,114@120@130,131,132 |
    | name2   | 110,111,112,113,114@120@130,131,132 |
    | name2   | 110,111,112,113,114@120@130,131,132 |
    | name2   | 110,111,112,113,114@120@130,131,132 |
    | name2   | 110,111,112,113,114@120@130,131,132 |
    | name2   | 110,111,112,113,114@120@130,131,132 |
    | name2   | 110,111,112,113,114@120@130,131,132 |
    | name2   | 110,111,112,113,114@120@130,131,132 |
    \-----------------------------------------------/
    

    【讨论】:

    • 仅对字段参考进行调整,效果非常好。谢谢!
    • 没问题,很高兴它对你有用 - 我相信还有更优雅的方法!
    猜你喜欢
    • 2018-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多