Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Joindre une colonne de données délimitée par des virgules

Idéalement, votre meilleure solution serait de normaliser Table2 afin de ne pas stocker une liste séparée par des virgules.

Une fois ces données normalisées, vous pouvez facilement interroger les données. La nouvelle structure du tableau pourrait ressembler à ceci :

CREATE TABLE T1
(
  [col1] varchar(2), 
  [col2] varchar(5),
  constraint pk1_t1 primary key (col1)
);

INSERT INTO T1
    ([col1], [col2])
VALUES
    ('C1', 'john'),
    ('C2', 'alex'),
    ('C3', 'piers'),
    ('C4', 'sara')
;

CREATE TABLE T2
(
  [col1] varchar(2), 
  [col2] varchar(2),
  constraint pk1_t2 primary key (col1, col2),
  constraint fk1_col2 foreign key (col2) references t1 (col1)
);

INSERT INTO T2
    ([col1], [col2])
VALUES
    ('R1', 'C1'),
    ('R1', 'C2'),
    ('R1', 'C4'),
    ('R2', 'C3'),
    ('R2', 'C4'),
    ('R3', 'C1'),
    ('R3', 'C4')
;

La normalisation des tables faciliterait grandement l'interrogation des données en joignant les tables :

select t2.col1, t1.col2
from t2
inner join t1
  on t2.col2 = t1.col1

Voir la démo

Ensuite, si vous souhaitez afficher les données sous forme de liste séparée par des virgules, vous pouvez utiliser FOR XML PATH et STUFF :

select distinct t2.col1, 
  STUFF(
         (SELECT distinct ', ' + t1.col2
          FROM t1
          inner join t2 t
            on t1.col1 = t.col2
          where t2.col1 = t.col1
          FOR XML PATH ('')), 1, 1, '') col2
from t2;

Voir la démo.

Si vous n'êtes pas en mesure de normaliser les données, vous pouvez faire plusieurs choses.

Tout d'abord, vous pouvez créer une fonction de fractionnement qui convertira les données stockées dans la liste en lignes pouvant être jointes. La fonction split serait similaire à ceci :

CREATE FUNCTION [dbo].[Split](@String varchar(MAX), @Delimiter char(1))       
returns @temptable TABLE (items varchar(MAX))       
as       
begin      
    declare @idx int       
    declare @slice varchar(8000)       

    select @idx = 1       
        if len(@String)<1 or @String is null  return       

    while @idx!= 0       
    begin       
        set @idx = charindex(@Delimiter,@String)       
        if @idx!=0       
            set @slice = left(@String,@idx - 1)       
        else       
            set @slice = @String       

        if(len(@slice)>0)  
            insert into @temptable(Items) values(@slice)       

        set @String = right(@String,len(@String) - @idx)       
        if len(@String) = 0 break       
    end   
return 
end;

Lorsque vous utilisez la fonction split, vous pouvez soit laisser les données dans plusieurs lignes, soit concaténer les valeurs dans une liste séparée par des virgules :

;with cte as
(
  select c.col1, t1.col2
  from t1
  inner join 
  (
    select t2.col1, i.items col2
    from t2
    cross apply dbo.split(t2.col2, ',') i
  ) c
    on t1.col1 = c.col2
) 
select distinct c.col1, 
  STUFF(
         (SELECT distinct ', ' + c1.col2
          FROM cte c1
          where c.col1 = c1.col1
          FOR XML PATH ('')), 1, 1, '') col2
from cte c

Voir la démo.

Une dernière façon d'obtenir le résultat consiste à appliquer FOR XML PATH directement.

select col1, 
(
  select ', '+t1.col2
  from t1
  where ','+t2.col2+',' like '%,'+cast(t1.col1 as varchar(10))+',%'
  for xml path(''), type
).value('substring(text()[1], 3)', 'varchar(max)') as col2
from t2;

Voir SQL Fiddle avec démo