2017-08-02 70 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 
相關問題