2017-08-02 107 views
1

我有一个像下面根据SQL Server中的列值(由逗号分隔)创建多个行吗?

Name Age  VisitedStaes 
----------------------------- 
A  20   NY, NJ, IL 
B  25   
C  25   NY, IL 

表这是可以生成使用SQL(Microsoft SQL Server的)以下类型的结果?

我想:

我的意思是,如果列(VisitedStaes)具有逗号sperated multipele值,它会根据

Name Age  VisitedStaes 
----------------------------- 
A  20   NY 
A  20   NJ 
A  20   IL 
B  25   
C  25   NY 
C  25   IL 

更新该列的单元格的值创建行做到这一点,但现在仍然没有找到任何解决方案 谢谢

我应用了函数(@scsimon),但问题是它只能为一些列做。不适用于下列图片的所有列。 enter image description here

+1

这已被要求,并回答了成百上千次...和你需要停止最大带走存储这样的数据。它违反了1NF会导致很大的痛苦。 –

回答

3

使用分离器...

declare @table table (Name char(1), Age int, VisitedStates varchar(64)) 
insert into @table 
values 
('A',20,'NY, NJ, IL'), 
('B',25,NULL),   
('C',25,'NY, IL') 


select 
    Name, 
    Age, 
    ltrim(Item) as VisitedStates 
from 
    @table 
cross apply dbo.DelimitedSplit8K(VisitedStates,',') x 

退货

+------+-----+---------------+ 
| Name | Age | VisitedStates | 
+------+-----+---------------+ 
| A | 20 | NY   | 
| A | 20 | NJ   | 
| A | 20 | IL   | 
| B | 25 | NULL   | 
| C | 25 | NY   | 
| C | 25 | IL   | 
+------+-----+---------------+ 

Jeff Moden Splitter

CREATE FUNCTION [dbo].[DelimitedSplit8K] (@pString VARCHAR(8000), @pDelimiter CHAR(1)) 
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE! 

RETURNS TABLE WITH SCHEMABINDING AS 
RETURN 

/* "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000... 
enough to cover VARCHAR(8000)*/ 

    WITH E1(N) AS (
       SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
       SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
       SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
       ),       --10E+1 or 10 rows 
     E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows 
     E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max 
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front 
        -- for both a performance gain and prevention of accidental "overruns" 
       SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4 
       ), 
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter) 
       SELECT 1 UNION ALL 
       SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter 
       ), 
cteLen(N1,L1) AS(--==== Return start and length (for use in substring) 
       SELECT s.N1, 
         ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000) 
        FROM cteStart s 
       ) 
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found. 
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1), 
     Item  = SUBSTRING(@pString, l.N1, l.L1) 
    FROM cteLen l 
; 
GO 
+0

临时表数据使用此功能是否有任何限制? – blueMoon

+0

否@blueMoon不用于拆分字符串数据 – scsimon

+0

我创建了一个临时表#TemperatureTable1,然后尝试应用此操作。如果我使用#TempTable1抛出错误。我的代码如下所示: 选择 \t LTRIM(项目)从#TempTable1 VisitedStates \t,#TempTable1.ProgramTitle 交叉应用dbo.DelimitedSplit8K(VisitedStates, '')× – blueMoon

1

Scismon将是我的第一次 选择。每个人都应该有一个良好的分流+1

但是,你不能使用,或者需要一个UDF,请考虑以下

Select A.Name 
     ,A.Age 
     ,VisitedStates = B.RetVal 
From YourTable A 
Outer Apply (
       Select RetSeq = Row_Number() over (Order By (Select null)) 
         ,RetVal = LTrim(RTrim(B2.i.value('(./text())[1]', 'varchar(max)'))) 
       From (Select x = Cast('<x>' + replace(A.VisitedStates,',','</x><x>')+'</x>' as xml).query('.')) as B1 
       Cross Apply x.nodes('x') AS B2(i) 
      ) B 

返回

Name Age VisitedStates 
A  20 NY 
A  20 NJ 
A  20 IL 
B  25 NULL 
C  25 NY 
C  25 IL 
+0

嘿约翰,我明白你的观点。我试图用你的。但问题是数据不能正确生成。我创建了一个临时表并存储了该表,但没有生成正确的结果。 – blueMoon

1

如果您使用SQL Server 2016或更高版本,那么您可以使用String_split,如下所示:

Select * from #data 
cross apply string_split(visitedstates,',') 

如果< = 2016,那么你可以按照以下查询:

Select [Name], Age, [Value] from (
    Select *, xm = CAST('<x>' + REPLACE((SELECT REPLACE(visitedstates,', ','$$$SSText$$$') AS [*] FOR XML PATH('')),'$$$SSText$$$','</x><x>')+ '</x>' AS XML) 
     from #data) d 
    cross apply (
     SELECT N.value(N'text()[1]', N'nvarchar(MAX)') as value FROM xm.nodes(N'x') as T(N) 
    ) a