Ces opérations sont généralement abordées avec une fonction qui sépare les valeurs de chaîne en utilisant une définition délimiteur. Si vous n'êtes pas sur SQL Server 2016+ et que vous ne pouvez pas utiliser le string_split
intégré, vous devrez lancer le vôtre. Personnellement, je l'utilise:
create function [dbo].[StringSplit]
(
@str nvarchar(4000) = ' ' -- String to split.
,@delimiter as nvarchar(1) = ',' -- Delimiting value to split on.
,@num as int = null -- Which value to return.
)
returns table
as
return
( -- Start tally table with 10 rows.
with n(n) as (select n from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n(n))
-- Select the same number of rows as characters in @str as incremental row numbers.
-- Cross joins increase exponentially to a max possible 10,000 rows to cover largest @str length.
,t(t) as (select top (select len(@str) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
-- Return the position of every value that follows the specified delimiter.
,s(s) as (select 1 union all select t+1 from t where substring(@str,t,1) = @delimiter)
-- Return the start and length of every value, to use in the SUBSTRING function.
-- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
,l(s,l) as (select s,isnull(nullif(charindex(@delimiter,@str,s),0)-s,4000) from s)
select rn as ItemNumber
,Item
from(select row_number() over(order by s) as rn
,substring(@str,s,l) as item
from l
) a
where rn = @num -- Return a specific value where specified,
or @num is null -- Or the everything where not.
)
qui est utilisé comme suit:
declare @Items table(IdItem nvarchar(50));
insert into @Items values('P'),('M, O'),('DI'),('M, P, DI, ZZZ'),('ZZZ');
declare @ItemDescripions table(ItemId nvarchar(50),ItemDescription nvarchar(50));
insert into @ItemDescripions values('I' ,'Injection'),('P' ,'Pill'),('DI','DIU'),('F' ,'AQVF'),('M' ,'AQVM'),('O' ,'Other');
select IdItem
,ltrim(rtrim(s.Item)) as Item
,isnull(d.ItemDescription,'Not Defined') as ItemDescription
from @Items i
outer apply dbo.StringSplit(i.IdItem,',',null) s
left join @ItemDescripions d
on(ltrim(rtrim(s.Item)) = d.ItemId);
Pour sortie:
+---------------+------+-----------------+
| IdItem | Item | ItemDescription |
+---------------+------+-----------------+
| P | P | Pill |
| M, O | M | AQVM |
| M, O | O | Other |
| DI | DI | DIU |
| M, P, DI, ZZZ | M | AQVM |
| M, P, DI, ZZZ | P | Pill |
| M, P, DI, ZZZ | DI | DIU |
| M, P, DI, ZZZ | ZZZ | Not Defined |
| ZZZ | ZZZ | Not Defined |
+---------------+------+-----------------+
Si vous voulez afficher exactement vos ItemDescription
valeurs que vous avez dans votre question (à laquelle je déconseillerais car cela viole les meilleures pratiques de base de données relationnelles sur ne pas stocker vos données dans les listes délimitées), vous pouvez utiliser un combo concaténer stuff
et for xml
à la place:
select i.IdItem
,stuff((select ', ' + isnull(d.ItemDescription,'Not Defined')
from dbo.StringSplit(i.IdItem,',',null) s
left join @ItemDescripions d
on(ltrim(rtrim(s.Item)) = d.ItemId)
order by s.ItemNumber
for xml path('')
)
,1,2,'') as ItemDescription
from @Items i;
Pour sortie:
+---------------+------------------------------+
| IdItem | ItemDescription |
+---------------+------------------------------+
| P | Pill |
| M, O | AQVM, Other |
| DI | DIU |
| M, P, DI, ZZZ | AQVM, Pill, DIU, Not Defined |
| ZZZ | Not Defined |
+---------------+------------------------------+