2009-06-04 4 views
6

Mes recherches Google sur la façon de découper une chaîne sur un delimiter ont donné lieu à certaines fonctions utiles pour les chaînes de fractionnement lorsque la chaîne est connue (voir ci-dessous):SQL 2005 de Split séparées par des virgules Colonne sur Délimiteur

SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
ALTER FUNCTION [dbo].[Split] (@String varchar(8000), @Delimiter char(1))  
    returns @temptable TABLE (items varchar(8000))  
    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 

Cela fonctionne bien pour une chaîne connue comme:

SELECT TOP 10 * FROM dbo.Split('This,Is,My,List',',') 

Cependant, je voudrais passer une colonne à une fonction, et avoir en même temps que mes filles fusionnées d'autres données dans sa propre ligne ... par exemple compte tenu des données :

CommaColumn ValueColumn1 ValueColumn2 
----------- ------------ ------------- 
ABC,123  1    2 
XYZ, 789  2    3 

Je voudrais écrire quelque chose comme:

SELECT Split(CommaColumn,',') As SplitValue, ValueColumn1, ValueColumn2 FROM MyTable 

Et retourner

SplitValue ValueColumn1 ValueColumn2 
---------- ------------ ------------ 
ABC   1    2 
123   1    2 
XYZ   2    3 
789   2    3 

Est-ce possible, ou quelqu'un at-il fait cela avant?

+1

99% du temps ou plus les colonnes séparées par des virgules sont le résultat d'une mauvaise base de données desi gn en premier lieu. Le seul endroit pour une fonction de split au niveau du serveur est de refactoriser ces colonnes dans leur propre table. –

+0

J'espère que cette table provient de votre base de données intermédiaire et qu'elle contient des données brutes provenant d'un système propriétaire dans lequel vous n'avez aucun moyen de modifier la disposition de la table. – VVS

Répondre

13

Oui, il est possible avec CROSS APPLY (SQL 2005+):

with testdata (CommaColumn, ValueColumn1, ValueColumn2) as (
    select 'ABC,123', 1, 2 union all 
    select 'XYZ, 789', 2, 3 
) 
select 
    b.items as SplitValue 
, a.ValueColumn1 
, a.ValueColumn2 
from testdata a 
cross apply dbo.Split(a.CommaColumn,',') b 

Notes:

  1. Vous devez ajouter un index à l'ensemble des résultats de votre colonne divisée, de façon à retourne deux colonnes, IndexNumber et Value.

  2. Les implémentations en ligne avec une table numérique sont généralement plus rapides que votre version procédurale ici.

par exemple:

create function [dbo].[Split] (@list nvarchar(max), @delimiter nchar(1) = N',') 
returns table 
as 
return (
    select 
    Number = row_number() over (order by Number) 
    , [Value] = ltrim(rtrim(convert(nvarchar(4000), 
     substring(@list, Number 
     , charindex(@delimiter, @[email protected], Number)-Number 
     ) 
    ))) 
    from dbo.Numbers 
    where Number <= convert(int, len(@list)) 
    and substring(@delimiter + @list, Number, 1) = @delimiter 
) 

Erland Sommarskog a la page définitive sur ce point, je pense: http://www.sommarskog.se/arrays-in-sql-2005.html

+0

Excelent Peter.J'ai sauvé mon ** de beaucoup d'ennuis :) –

10

Corrigez-le dans le bon sens - faites de cette colonne une table associée. Aucun bien ne vient jamais de colonnes scalaires séparées par des virgules.

+0

Malheureusement pas ma situation idéale, mais toujours correcte néanmoins. –

0

Vous pouvez essayer quelque chose comme:

SELECT s.Items AS SplitValue, ValueColumn1, ValueColumn2 
FROM MyTable, Split(CommaColumn,',') AS s 
+0

Cela pourrait effectivement fonctionner .. Je pense. – VVS

+0

Ugh, mauvaise syntaxe de jointure aussi. –

+0

Essayé cela, qui n'a malheureusement pas fonctionné .. merci pour votre tentative. –

1

+1 aux commentaires anti-CSV, mais si vous devez le faire vous utiliseriez CROSS APPLY ou OUTER APPLY.

1
alter procedure [dbo].[usp_split](@strings varchar(max)) as 
begin 
    Declare @index int 
    set @index=1 
    declare @length int 
    set @length=len(@strings) 
    declare @str varchar(max) 
    declare @diff int 
    declare @Tags table(id varchar(30)) 
    while(@index<@length) 
    begin 
     if(@index='1') 
     begin 
      set @str=(SELECT substring(@strings, @index, (charindex(',',(substring(@strings, @index,(@length)))))-1)) 
      insert into @Tags values(@str) 
       set @index=(charindex(',',(substring(@strings, @index,(@length))))) 
     end 
     else 
     begin 
      set @[email protected]@index 
      if(@diff !=0) 
      begin 
       set @str=(select substring(@strings, @index, (charindex(',',(substring(@strings,@index,@diff))))-1)) 
       if(@str is not null and @str!='') 
       begin 
        insert into @Tags VALUES(@str) 
       end 
       set @[email protected] +(charindex(',',(substring(@strings, @index,@diff)))) 
      end 
     end 
    end 
    set @str=(select right(@strings,(charindex(',',(substring(reverse(@strings),1,(@length)))))-1)) 
    insert into @Tags VALUES(@str) 
    select id from @Tags 
end 

Utilisation:

exec usp_split '1212,21213,1,3,133,1313131,1,231313,5' 
Questions connexes