SQL - 在条件分隔符的 STUFF 函数中的 CASE WHEN
SQL - CASE WHEN within STUFF function for conditional delimiter
长话短说:我正在尝试在我的 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
...其中 returns:
| 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() 之前和 "series" 结束的地方进行比较,只需添加 '+ @',它会在 STUFF() 期间被拉入。
任何帮助或创造性方法将不胜感激。提前致谢。
它不是很漂亮,但我认为这可行:
-- 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 |
\-----------------------------------------------/
您应该可以使用 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
长话短说:我正在尝试在我的 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
...其中 returns:
| 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() 之前和 "series" 结束的地方进行比较,只需添加 '+ @',它会在 STUFF() 期间被拉入。
任何帮助或创造性方法将不胜感激。提前致谢。
它不是很漂亮,但我认为这可行:
-- 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 |
\-----------------------------------------------/
您应该可以使用 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