[SQL] 행 구분 기호 및 열 구분 SQL 서버에서 주어진 테이블에 분할 문자열
SQL행 구분 기호 및 열 구분 SQL 서버에서 주어진 테이블에 분할 문자열
어떻게 SQL Server의 테이블에 매트릭스를 포함하는 문자열을 분할하려면? 문자열 컬럼 및 행 구분 기호가 있습니다.
내가 문자열이 있다고 가정 :
declare @str varchar(max)='A,B,C;D,E,F;X,Y,Z';
(세 개의 열에서) 예상 결과 :
+---+---+---+
| A | B | C |
+---+---+---+
| D | E | F |
+---+---+---+
| X | Y | Z |
+---+---+---+
나는 열과 행의 수를 정의하지 않은 일반 솔루션을 찾고 있어요. 문자열 그래서 :
declare @str varchar(max)='A,B;D,E';
두 개의 열이있는 테이블로 분할됩니다 :
+---+---+
| A | B |
+---+---+
| D | E |
+---+---+
내 노력. 내 첫번째 생각에 문자열을집니다 동적 SQL을 사용하는 것이 었습니다 : dbo.temp 값 (...) 처음 컬럼의 오른쪽 수있는 테이블을 만들 필요하기 때문에 매우 빠른 미성년자의 단점을 가지고 있지만이 접근 방식에 삽입합니다. 단지 짧은 질문을 유지하기 위해 아래 나는 내 자신의 질문에 대한 답변에서이 방법을 제시 하였다.
또 다른 아이디어는에서 다음 서버 및 대량 삽입에 CSV 파일에 문자열을 작성하는 것입니다. 나는 그것을 수행하는 방법을 알고하지 않습니다하지만 무슨 일이 제 1 및 제 2 아이디어의 성능 일 것이다.
내가 SQL 서버에 엑셀에서 데이터 가져 오기를 원하기 때문에 나는이 질문을하는 이유입니다. 나는 다른 ADO 방식으로 실험 한 바와 같이, 매트릭스 문자열을 보내는이 방법은 문자열의 증가, 특히 길이, 압도적 인 승리입니다. 여기 질문의 젊은 쌍둥이 형제를 질문 : 어떻게 엑셀 범위에서 같은 문자열을 준비하는 제안을 찾을 수 VBA 문자열로 엑셀 범위를 설정합니다.
바운티 나는 수상 매트로 결정했다. 내가보기 엔 숀 랭의 답변을 정도. 당신에게 숀 감사드립니다. 나는 그것의 단순성과 곤란을 위해 매트의 답변을 좋아했다. (마지막으로, 몇 개월 후, 내가 수락 매트의 대답 갱신) 내가 어떤 대답을 수락하지하고 당분간 있도록 매트의 숀의에서 떨어져 다른 접근 방식은 병렬로 사용 될 수 있습니다. 나는 내가 시작했다 대답의 멋진 진화가 들어 값으로 자신의 아이디어를 아메드 사이드 감사하고 싶습니다. 물론, 그것은 매트 나 숀의에 일치하지 않습니다. 나는 모든 대답을 upvoted. 나는이 방법을 사용하여 당신에게서 어떤 의견을 보내 주셔서 감사합니다. 퀘스트 주셔서 감사합니다.
해결법
-
==============================
1.쉬운 방법 중 하나는 구분 기호를 교체를 기반으로 XML로 문자열을 변환하는 것입니다.
쉬운 방법 중 하나는 구분 기호를 교체를 기반으로 XML로 문자열을 변환하는 것입니다.
declare @str varchar(max)='A,B,C;D,E,F;X,Y,Z'; DECLARE @xmlstr XML SET @xmlstr = CAST(('<rows><row><col>' + REPLACE(REPLACE(@str,';','</col></row><row><col>'),',','</col><col>') + '</col></row></rows>') AS XML) SELECT t.n.value('col[1]','CHAR(1)') as Col1 ,t.n.value('col[2]','CHAR(1)') as Col2 ,t.n.value('col[3]','CHAR(1)') as Col3 FROM @xmlstr.nodes ('/rows/row') AS t(n)
또는 동적으로
DECLARE @str varchar(max)='A,B,C,D,E;F,G,H,I,J;K,L,M,N,O'; DECLARE @NumOfColumns INT SET @NumOfColumns = (LEN(@str) - LEN(REPLACE(@str,',',''))) / (LEN(@str) - LEN(REPLACE(@str,';','')) + 1) + 1 DECLARE @xmlstr XML SET @xmlstr = CAST(('<rows><row><col>' + REPLACE(REPLACE(@str,';','</col></row><row><col>'),',','</col><col>') + '</col></row></rows>') AS XML) DECLARE @ParameterDef NVARCHAR(MAX) = N'@XMLInputString xml' DECLARE @SQL NVARCHAR(MAX) = 'SELECT ' DECLARE @i INT = 1 WHILE @i <= @NumOfColumns BEGIN SET @SQL = @SQL + IIF(@i > 1,',','') + 't.n.value(''col[' + CAST(@i AS VARCHAR(10)) + ']'',''NVARCHAR(MAX)'') as Col' + CAST(@i AS VARCHAR(10)) SET @i = @i + 1 END SET @SQL = @SQL + ' FROM @XMLInputString.nodes (''/rows/row'') AS t(n)' EXECUTE sp_executesql @SQL,@ParameterDef,@XMLInputString = @xmlstr
-
==============================
2.내가 어떤 루프없이이 작업을 수행 할 수 있는지를 결정하므로 확인이 퍼즐은 나에게 흥미. 작업이 전제 조건이 몇 가지 있습니다. 첫 번째는 우리는 당신이 집계 테이블의 일종 있다고 가정 것이다. 경우 여기에 해당이없는 광산에 대한 코드입니다. 나는 내가 사용하는 모든 시스템에 보관하십시오.
내가 어떤 루프없이이 작업을 수행 할 수 있는지를 결정하므로 확인이 퍼즐은 나에게 흥미. 작업이 전제 조건이 몇 가지 있습니다. 첫 번째는 우리는 당신이 집계 테이블의 일종 있다고 가정 것이다. 경우 여기에 해당이없는 광산에 대한 코드입니다. 나는 내가 사용하는 모든 시스템에 보관하십시오.
create View [dbo].[cteTally] as WITH E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)), 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 ( SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4 ) select N from cteTally
이 퍼즐의 두번째 조각 세트 기반 문자열 스플리터를 필요로한다. 이것에 대한 내 취향은 동네 짱의 빠른 제프 MODEN 스플리터입니다. 한 가지주의해야 할 점은 단지 최대 8,000 VARCHAR 값으로 작동한다는 것입니다. 이것은 내가 작업을 가장 구분되는 스트링에 대한 충분한입니다. 현재 제프 MODEN의 스플리터 (DelimitedSplit8K를) 찾을 수 있습니다.
http://www.sqlservercentral.com/articles/Tally+Table/72993/
마지막으로는 없습니다 적어도 내가 여기에 사용하고있는 기술은 동적 크로스 탭 것입니다. 이 제프 MODEN에서 배운 것을 다른 I입니다. 그는 여기에 주제에 대한 좋은 기사가 있습니다.
http://www.sqlservercentral.com/articles/Crosstab/65048/
함께 당신이 정말 빠른 것 잘 확장 할 수있는이 같은 것을 가지고 올 수이 모든 퍼팅.
declare @str varchar(max)='A,B,C;D,E,F;X,Y,Z'; declare @StaticPortion nvarchar(2000) = 'declare @str varchar(max)=''' + @str + ''';with OrderedResults as ( select s.ItemNumber , s.Item as DelimitedValues , x.ItemNumber as RowNum , x.Item from dbo.DelimitedSplit8K(@str, '';'') s cross apply dbo.DelimitedSplit8K(s.Item, '','') x ) select ' declare @DynamicPortion nvarchar(max) = ''; declare @FinalStaticPortion nvarchar(2000) = ' from OrderedResults group by ItemNumber'; select @DynamicPortion = @DynamicPortion + ', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then Item end) as Column' + CAST(N as varchar(6)) + CHAR(10) from cteTally t where t.N <= (select MAX(len(Item) - LEN(replace(Item, ',', ''))) + 1 from dbo.DelimitedSplit8K(@str, ';') ) declare @SqlToExecute nvarchar(max) = @StaticPortion + stuff(@DynamicPortion, 1, 1, '') + @FinalStaticPortion exec sp_executesql @SqlToExecute
--편집하다--
여기 링크가 잘못 될 경우에 DelimitedSplit8K 기능입니다.
ALTER FUNCTION [dbo].[DelimitedSplit8K] --===== Define I/O parameters (@pString VARCHAR(8000), @pDelimiter CHAR(1)) RETURNS TABLE WITH SCHEMABINDING AS RETURN --===== "Inline" CTE Driven "Tally Table" produces values from 0 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 "zero base" and limits the number of rows right up front -- for both a performance gain and prevention of accidental "overruns" SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(@pString,1))) 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 t.N+1 FROM cteTally t WHERE (SUBSTRING(@pString,t.N,1) = @pDelimiter OR t.N = 0) ) --===== 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 s.N1), Item = SUBSTRING(@pString,s.N1,ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)) FROM cteStart s ;
-
==============================
3.다음 코드는 SQL 서버에서 작동합니다. 그것은 약간의 조작으로 공통 테이블 표현식 및 동적 SQL을 사용합니다. 그냥 @str 변수에 문자열 값을 할당하고 한 번에 전체 코드를 실행합니다. 이 CTE를 사용하기 때문에, 각각의 단계에서 데이터 분석을 용이하다.
다음 코드는 SQL 서버에서 작동합니다. 그것은 약간의 조작으로 공통 테이블 표현식 및 동적 SQL을 사용합니다. 그냥 @str 변수에 문자열 값을 할당하고 한 번에 전체 코드를 실행합니다. 이 CTE를 사용하기 때문에, 각각의 단계에서 데이터 분석을 용이하다.
Declare @Str varchar(max)= 'A,B,C;D,E,F;X,Y,Z'; IF OBJECT_ID('tempdb..#RawData') IS NOT NULL DROP TABLE #RawData; ;WITH T_String AS ( SELECT RIGHT(@Str,LEN(@Str)-CHARINDEX(';',@Str,1)) AS RawString, LEFT(@Str,CHARINDEX(';',@Str,1)-1) AS RowString, 1 AS CounterValue, len(@Str) - len(replace(@Str, ';', '')) AS RowSize -- UNION ALL -- SELECT IIF(CHARINDEX(';',RawString,1)=0,NULL,RIGHT(RawString,LEN(RawString)-CHARINDEX(';',RawString,1))) AS RawString, IIF(CHARINDEX(';',RawString,1)=0,RawString,LEFT(RawString,CHARINDEX(';',RawString,1)-1)) AS RowString, CounterValue+1 AS CounterValue, RowSize AS RowSize FROM T_String AS r WHERE CounterValue <= RowSize ) ,T_Columns AS ( SELECT RowString AS RowValue, RIGHT(a.RowString,LEN(a.RowString)-CHARINDEX(',',a.RowString,1)) AS RawString, LEFT(a.RowString,CHARINDEX(',',a.RowString,1)-1) AS RowString, 1 AS CounterValue, len(a.RowString) - len(replace(a.RowString, ',', '')) AS RowSize FROM T_String AS a --WHERE a.CounterValue = 1 -- UNION ALL -- SELECT RowValue, IIF(CHARINDEX(',',RawString,1)=0,NULL,RIGHT(RawString,LEN(RawString)-CHARINDEX(',',RawString,1))) AS RawString, IIF(CHARINDEX(',',RawString,1)=0,RawString,LEFT(RawString,CHARINDEX(',',RawString,1)-1)) AS RowString, CounterValue+1 AS CounterValue, RowSize AS RowSize FROM T_Columns AS r WHERE CounterValue <= RowSize ) ,T_Data_Prior2Pivot AS ( SELECT c.RowValue, c.RowString, c.CounterValue FROM T_Columns AS c INNER JOIN T_String AS r ON r.RowString = c.RowValue ) SELECT * INTO #RawData FROM T_Data_Prior2Pivot; DECLARE @columnNames VARCHAR(MAX) ,@sqlQuery VARCHAR(MAX) SELECT @columnNames = COALESCE(@columnNames+', ['+CAST(CounterValue AS VARCHAR)+']','['+CAST(CounterValue AS VARCHAR)+']') FROM (SELECT DISTINCT CounterValue FROM #RawData) T PRINT @columnNames SET @sqlQuery = ' SELECT '+@columnNames+' FROM ( SELECT * FROM #RawData ) AS b PIVOT (MAX(RowString) FOR CounterValue IN ('+@columnNames+')) AS p ' EXEC (@sqlQuery);
다음은 http://statisticsparser.com/에서 위의 질의에 대한 통계의 스크린 샷이다.
-
==============================
4.
**--Using dynamic queries..** declare @str varchar(max)='A,B,C;D,E,F;X,Y,Z'; declare @cc int select @cc = len (substring (@str, 0, charindex(';', @str))) - len(replace(substring (@str, 0, charindex(';', @str)), ',', '')) declare @ctq varchar(max) = 'create table t(' declare @i int = 0 while @i <= @cc begin select @ctq = @ctq + 'column' + char(65 + @i) + ' varchar(max), ' select @i = @i + 1 end select @ctq = @ctq + ')' select @str = '''' + replace(@str, ',', ''',''') + '''' select @str = 'insert t select ' + @str select @str = replace (@str, ';', ''' union all select ''') exec(@ctq) exec(@str)
-
==============================
5.난 그냥 질문을 할 때 내가 시간에 사용하는 것을 보여주고, 질문을 확장하는 내 질문에 대한 답변을 게시 할 수 있습니다.
난 그냥 질문을 할 때 내가 시간에 사용하는 것을 보여주고, 질문을 확장하는 내 질문에 대한 답변을 게시 할 수 있습니다.
아이디어로 원래 문자열을 변경하는 것입니다 :
insert into dbo.temp values (...)(...)
여기에 대한 저장 프로 시저입니다 :
create PROC [dbo].[StringToMatrix] ( @String nvarchar(max) ,@DelimiterCol nvarchar(50)=',' ,@DelimiterRow nvarchar(50)=';' ,@InsertTable nvarchar(200) ='dbo.temp' ,@Delete int=1 --delete is ON ) AS BEGIN set nocount on; set @String = case when right(@String,len(@DelimiterRow))=@DelimiterRow then left(@string,len(@String)-len(@DelimiterRow)) else @String end --if present, removes the last row delimiter at the very end of string set @String = replace(@String,@DelimiterCol,''',''') set @String = replace(@String,@DelimiterRow,'''),'+char(13)+char(10)+'(''') set @String = 'insert into '+@InsertTable+' values '+char(13)+char(10)+'(''' +@String +''');' set @String = replace(@String,'''''','null') --optional, changes empty strings to nulls set @String = CASE WHEN @Delete = 1 THEN 'delete from '+@InsertTable+';'+char(13)+char(10)+@String ELSE @String END --print @String exec (@String) END
코드와 시저를 실행 :
exec [dbo].[StringToMatrix] 'A,B,C;D,E,F;X,Y,Z'
다음 @String을 생성합니다 :
delete from [dbo].[temp]; insert into [dbo].[temp] values ('A','B','C'), ('D','E','F'), ('X','Y','Z');
PROC의 마지막 행에있는 동적 실행된다.
용액 숫자가 삽입되기에 적합한 제 dbo.table을 만들 필요하다. 그것은 약간의 단점이다. dbo.temp으로 선택 *는 구조를 가지고있는 경우가있을 수 있도록 동적 따라서 용액이 아니다. 그럼에도 불구하고 나는 그것이 작동하기 때문에이 솔루션을 공유 할 그것은 빠르고, 간단하고, 어쩌면 다른 답변에 대한 영감이 될 것입니다.
-
==============================
6.이 문제는 임시 테이블, 뷰, 루프 또는 XML에 대한 필요없이 해결 될 수있다. 먼저 아래의 예에 따라 집계 테이블을 기반으로 문자열 스플리터 기능을 만들 수 있습니다 :
이 문제는 임시 테이블, 뷰, 루프 또는 XML에 대한 필요없이 해결 될 수있다. 먼저 아래의 예에 따라 집계 테이블을 기반으로 문자열 스플리터 기능을 만들 수 있습니다 :
ALTER FUNCTION [dbo].[SplitString] ( @delimitedString VARCHAR(MAX), @delimiter VARCHAR(255) ) RETURNS TABLE WITH SCHEMABINDING AS RETURN 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), E2(N) AS (SELECT 1 FROM E1 a, E1 b), E4(N) AS (SELECT 1 FROM E2 a, E2 b), E42(N) AS (SELECT 1 FROM E4 a, E2 b), cteTally(N) AS (SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(@delimitedString,1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42), cteStart(N1) AS (SELECT t.N+1 FROM cteTally t WHERE (SUBSTRING(@delimitedString,t.N,1) = @delimiter OR t.N = 0)) SELECT ROW_NUMBER() OVER (ORDER BY s.N1) AS Nr ,Item = SUBSTRING(@delimitedString, s.N1, ISNULL(NULLIF(CHARINDEX(@delimiter,@delimitedString,s.N1),0)-s.N1,8000)) FROM cteStart s;
먼저 스플릿 행 구분에 기초하여, 문자열을 분할 기능을 사용한다. 그런 다음 외부 문을 적용하여 각 행을 다시 분할 기능을 적용 할 수 있습니다. 마지막으로 결과를 돌리십시오. 열의 수를 알 수 있기 때문에 쿼리는 아래의 예에 따라 동적 SQL로 실행되어야 할 것이다 :
DECLARE @source VARCHAR(max) = 'A1,B1,C1,D1,E1,F1,G1;A2,B2,C2,D2,E2,F2,G2;A3,B3,C3,D3,E3,F3,G3;A4,B4,C4,D4,E4,F4,G4;A5,B5,C5,D5,E5,F5,G5;A6,B6,C6,D6,E6,F6,G6;A7,B7,C7,D7,E7,F7,G7;A8,B8,C8,D8,E8,F8,G8;A9,B9,C9,D9,E9,F9,G9;A10,B10,C10,D10,E10,F10,G10;A11,B11,C11,D11,E11,F11,G11;A12,B12,C12,D12,E12,F12,G12;A13,B13,C13,D13,E13,F13,G13;A14,B14,C14,D14,E14,F14,G14;A15,B15,C15,D15,E15,F15,G15;A16,B16,C16,D16,E16,F16,G16;A17,B17,C17,D17,E17,F17,G17;A18,B18,C18,D18,E18,F18,G18;A19,B19,C19,D19,E19,F19,G19;A20,B20,C20,D20,E20,F20,G20' -- First determine the columns names. Since the string can be potential very long we don’t want to parse the entire string to determine how many columns -- we have, instead get sub string of main string up to first row delimiter. DECLARE @firstRow VARCHAR(max) = LEFT(@source, CHARINDEX(';', @source) - 1); DECLARE @columnNames NVARCHAR(MAX) = ''; -- Use string splitter function on sub string to determine column names. SELECT @columnNames = STUFF(( SELECT ',' + QUOTENAME(CAST(ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS VARCHAR(10))) FROM [dbo].[SplitString](@firstRow, ',') Items FOR XML PATH('')), 1, 1, ''); -- Next build dynamic query that will generate our matrix table. -- CTE first split string by row delimiters then it applies the string split function again on each row. DECLARE @pivotQuery NVARCHAR(MAX) =' ;WITH CTE_SplitData AS ( SELECT R.Nr AS [Row] ,C.[Columns] ,ROW_NUMBER() OVER (PARTITION BY R.Nr ORDER BY R.Item) AS ColumnNr FROM [dbo].[SplitString](@source, '';'') R OUTER APPLY ( SELECT Item AS [Columns] FROM [dbo].[SplitString](R.Item, '','') ) C ) -- Pivoted reuslt SELECT * FROM ( SELECT * FROM CTE_SplitData )as T PIVOT ( max(T.[Columns]) for T.[ColumnNr] in (' + @columnNames + ') ) as P' EXEC sp_executesql @pivotQuery, N'@source VARCHAR(MAX)', @source = @source; -- Pass the source string to be split as a parameter to the dynamic query.
-
==============================
7.회전 및 동적 SQL 일부 XML.
회전 및 동적 SQL 일부 XML.
다음 쿼리는 다음과 같습니다
--declare @str varchar(max)='A,B;D,E;X,Y', declare @str varchar(max)='A,B,C;D,E,F;X,Y,Z', @x xml, @col nvarchar(max), @sql nvarchar(max), @params nvarchar(max) = '@x xml, @i int', @i int SELECT @x = CAST('<row>'+REPLACE(('<p>'+REPLACE(@str,',','</p><p>')+'</p>'),';','</p></row><row><p>')+'</row>' as xml), @str = REPLACE(@str,';',',;')+',;', @i = (LEN(@str)-LEN(REPLACE(@str,',','')))/(LEN(@str)-LEN(REPLACE(@str,';',''))) ;WITH colsPiv AS ( SELECT 1 as col UNION ALL SELECT col+1 FROM colsPiv WHERE col < @i ) SELECT @col = ( SELECT ','+QUOTENAME(col) FROM colsPiv FOR XML PATH('') ) SELECT @sql = N' ;WITH cte AS ( SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) RowNum, t.c.value(''.'',''nvarchar(max)'') as [Values] FROM @x.nodes(''/row/p'') as t(c) ) SELECT '+STUFF(@col,1,1,'')+' FROM ( SELECT RowNum - CASE WHEN RowNum%@i = 0 THEN @i ELSE RowNum%@i END Seq , CASE WHEN RowNum%@i = 0 THEN @i ELSE RowNum%@i END as [ColumnNum], [Values] FROM cte ) as t PIVOT ( MAX([Values]) FOR [ColumnNum] IN ('+STUFF(@col,1,1,'')+') ) as pvt' EXEC sp_executesql @sql, @params, @x = @x, @i = @i
D, E, F; A, B, C에 대한 출력 X, Y, Z :
1 2 3 A B C D E F X Y Z
A, B를 들어, D, E, X, Y :
1 2 A B D E X Y
-
==============================
8.이 솔루션에 나는 가능한 한 문자열 조작을 사용합니다. 절차는 키워드 VALUES에 적합한 형태로 입력 된 문자열로 변환하여 동적 SQL 문을 구성하는 열 수를 카운트함으로써 열 타이틀을 구축하고, 필요한 헤더를 생성한다. 그런 다음, 간단하게 구성된 SQL 문을 실행합니다.
이 솔루션에 나는 가능한 한 문자열 조작을 사용합니다. 절차는 키워드 VALUES에 적합한 형태로 입력 된 문자열로 변환하여 동적 SQL 문을 구성하는 열 수를 카운트함으로써 열 타이틀을 구축하고, 필요한 헤더를 생성한다. 그런 다음, 간단하게 구성된 SQL 문을 실행합니다.
Create Proc dbo.Spliter ( @str varchar(max), @RowSep char(1), @ColSep char(1) ) as declare @FirstRow varchar(max), @hdr varchar(max), @n int, @i int=0 -- Generate the Column names set @FirstRow=iif(CHARINDEX(@RowSep, @str)=0, @str, Left(@str, CHARINDEX(@RowSep, @str)-1)) set @n=LEN(@FirstRow) - len(REPLACE(@FirstRow, @ColSep,'')) while @i<=@n begin Set @hdr= coalesce(@hdr+', ', '') + 'Col' +convert(varchar, @i) set @i+=1 end --Convert the input string to a form suitable for Values keyword --i.e. similar to Values(('A'),('B'),('C')),(('D'),('E'),('F')), ...etc set @str =REPLACE(@str, @ColSep,'''),(''') set @str = 'Values((''' + REPLACE(@str, @RowSep, ''')),((''') + '''))' exec('SELECT * FROM (' + @str + ') as t('+@hdr+')') -- exec dbo.Spliter 'A,B,C;D,E,F;X,Y,Z', ';', ','
방법-2 :
값은 같은 PrzemyslawRemin로 표시 제한하는 1,000 행의 문제를 극복하기 위해, 여기에 하나의 행의 XML 필드로 변환 입력 문자열에 작은 수정하고 다음 CROSS은 개별 요소에 적용합니다.
Create Proc dbo.Spliter2 ( @str varchar(max), @RowSep char(1), @ColSep char(1) ) as declare @FirstRow varchar(max), @hdr varchar(max), @ColCount int, @i int=0 , @ColTemplate varchar(max)= 'Col.value(''(./c)[$]'', ''VARCHAR(max)'') AS Col$' -- Determin the number of columns set @FirstRow=iif(CHARINDEX(@RowSep, @str)=0, @str, Left(@str, CHARINDEX(@RowSep, @str)-1)) set @ColCount = LEN(@FirstRow) - len(REPLACE(@FirstRow, @ColSep,'')) -- Construct Column Headers by replacing the $ with the column number -- similar to: Col.value('(./c)[1]', 'VARCHAR(max)') AS Col1, Col.value('(./c)[2]', 'VARCHAR(max)') AS Col2 while @i<=@ColCount begin Set @hdr= coalesce(@hdr+', ', '') + Replace(@ColTemplate, '$', convert(varchar, @i+1)) set @i+=1 end -- Convert the input string to XML format -- similar to '<r><c>A</c><c>B</c><c>c</c></r> <r><c>D</c><c>E</c><c>f</c> </r> set @str='<c>'+replace(@str, ',', '</c>'+'<c>')+'</c>' set @str='<r>'+replace(@str , ';', '</c></r><r><c>')+'</r>' set @str='SELECT ' +@HDR + ' From(Values(Cast('''+@str+''' as xml))) as t1(x) CROSS APPLY x.nodes(''/r'') as t2(Col)' exec( @str) -- exec dbo.Spliter2 'A,B,C;D,E,F;X,Y,Z', ';', ','
-
==============================
9.여기에 또 다른 접근 방법이다.
여기에 또 다른 접근 방법이다.
Declare @Str varchar(max)='A,B,C;D,E,F;X,Y,Z'; Select A.*,B.* Into #TempSplit From (Select RowNr=RetSeq,String=RetVal From [dbo].[udf-Str-Parse](@Str,';')) A Cross Apply [dbo].[udf-Str-Parse](A.String,',') B Declare @SQL varchar(max) = '' Select @SQL = @SQL+Concat(',Col',RetSeq,'=max(IIF(RetSeq=',RetSeq,',RetVal,null))') From (Select Distinct RetSeq from #TempSplit) A Order By A.RetSeq Set @SQL =' If Object_ID(''[dbo].[Temp]'', ''U'') IS NOT NULL Drop Table [dbo].[Temp]; Select ' + Stuff(@SQL,1,1,'') + ' Into [dbo].[Temp] From #TempSplit Group By RowNr Order By RowNr ' Exec(@SQL) Select * from Temp
보고
Col1 Col2 Col3 A B C D E F X Y Z
이제이 아래에 나열되어있는 파서를 필요로하지 않습니다 :
CREATE FUNCTION [dbo].[udf-Str-Parse] (@String varchar(max),@Delimiter varchar(10)) Returns Table As Return ( Select RetSeq = Row_Number() over (Order By (Select null)) ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)'))) From (Select x = Cast('<x>'+ Replace(@String,@Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A Cross Apply x.nodes('x') AS B(i) ); --Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',') --Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
그냥 설명하기 위해 먼저 구문 분석이 돌아갑니다
RowNr String 1 A,B,C 2 D,E,F 3 X,Y,Z
이 후 다음을 반환하고 임시 테이블에 저장되는 크로스가 적용을 통해 다시 구문 분석
RowNr String RetSeq RetVal 1 A,B,C 1 A 1 A,B,C 2 B 1 A,B,C 3 C 2 D,E,F 1 D 2 D,E,F 2 E 2 D,E,F 3 F 3 X,Y,Z 1 X 3 X,Y,Z 2 Y 3 X,Y,Z 3 Z
Declare @String varchar(max)='A,B,C;D,E,F;X,Y,Z'; Declare @SQL varchar(max) = '',@Col int = Len(Left(@String,CharIndex(';',@String)-1))-Len(replace(Left(@String,CharIndex(';',@String)-1),',',''))+1 Select @SQL = @SQL+SQL From (Select Top (@Col) SQL=Concat(',xRow.xNode.value(''col[',N,']'',''varchar(max)'') as Col',N) From (Select N From (Values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) N(N) ) N ) A Select @SQL = Replace('Declare @XML XML = Cast((''<row><col>'' + Replace(Replace(''[getString]'','';'',''</col></row><row><col>''),'','',''</col><col>'') + ''</col></row>'') as XML);Select '+Stuff(@SQL,1,1,'')+' From @XML.nodes(''/row'') AS xRow(xNode) ','[getString]',@String) Exec (@SQL)
보고
Col1 Col2 Col3 A B C D E F X Y Z
-
==============================
10.여기서 분할 지정 기능을 이용하여 동적 PIVOT 통해이를 수행하는 방법은 :
여기서 분할 지정 기능을 이용하여 동적 PIVOT 통해이를 수행하는 방법은 :
분할 기능
CREATE FUNCTION [dbo].[fn_Split](@text varchar(MAX), @delimiter varchar(20) = ' ') RETURNS @Strings TABLE ( position int IDENTITY PRIMARY KEY, value varchar(MAX) ) AS BEGIN DECLARE @index int SET @index = -1 WHILE (LEN(@text) > 0) BEGIN SET @index = CHARINDEX(@delimiter , @text) IF (@index = 0) AND (LEN(@text) > 0) BEGIN INSERT INTO @Strings VALUES (@text) BREAK END IF (@index > 1) BEGIN INSERT INTO @Strings VALUES (LEFT(@text, @index - 1)) SET @text = RIGHT(@text, (LEN(@text) - @index)) END ELSE SET @text = RIGHT(@text, (LEN(@text) - @index)) END RETURN END GO
질문
Declare @Str Varchar (Max) = 'A,B,C;D,E,F;X,Y,Z'; Declare @Sql NVarchar (Max) = '', @Cols NVarchar (Max) = ''; ;With Rows As ( Select Position, Value As Row From dbo.fn_Split(@str, ';') ), Columns As ( Select Rows.Position As RowNum, Cols.Position As ColNum, Cols.Value As ColValue From Rows Cross Apply dbo.fn_Split(Row, ',') Cols ) Select * Into #Columns From Columns Select @Cols = Stuff(( Select Distinct ',' + QuoteName(ColNum) From #Columns For Xml Path(''), Type).value('.', 'NVARCHAR(MAX)') , 1, 1, '') Select @SQL = 'SELECT ' + @Cols + ' FROM #Columns Pivot ( Max(ColValue) For ColNum In (' + @Cols + ') ) P Order By RowNum' Execute (@SQL)
결과
1 2 3 A B C D E F X Y Z
-
==============================
11.내 솔루션 string_split 물건을 사용하고 있습니다 .. 첫째가 어떻게 작동하는지에 대한 예입니다
내 솔루션 string_split 물건을 사용하고 있습니다 .. 첫째가 어떻게 작동하는지에 대한 예입니다
DECLARE @str varchar(max) = 'A,B,C;D,E,F;X,Y,Z'; ;WITH cte AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn, * FROM string_split(@str, ';')), cte2 AS (SELECT rn, ROW_NUMBER() OVER (PARTITION BY rn ORDER BY (SELECT NULL)) rownum, val.value FROM cte c CROSS APPLY string_split(value, ',') val) SELECT [1], [2], [3] FROM cte2 PIVOT (MAX(value) FOR rownum IN ([1], [2], [3])) p
동적를 사용하여 우리는 열 목록을 확인할 수 있으며 모든 입력을 위해 작동 할 SQL
declare @str varchar(max)='A,B;D,E;X,Y'; declare @sql nvarchar(max) declare @cols varchar(max) = '' ;with cte as ( select row_number() over(order by (select null)) rn from string_split( substring(@str,1,charindex(';', @str)-1),',') ) select @cols=concat(@cols,',',quotename(rn)) from cte select @cols = stuff(@cols,1,1,'') set @sql = N' declare @str varchar(max)=''A,B;D,E;X,Y''; with cte as ( select row_number() over( order by (select null)) as rn, * from string_split(@str,'';'') ), cte2 as ( select rn, row_number() over(partition by rn order by (select null)) rownum, val.value from cte c cross apply string_split(value,'','') val ) select ' +@cols + ' from cte2 pivot (max(value) for rownum in (' + @cols + ')) p ' exec sp_executesql @sql
SQL Server를 사용하는 경우 <2016 우리는 우리 자신의 분할 기능을 쓸 수 있습니다
-
==============================
12.다음은 영업 이익이 질문 꽤 무엇 아니지만, 그것은 올바른 열 이름을 가진 SQL 테이블에 열 머리글로 CSV (실제로 탭-SV)과 같은 스프레드 시트를 수출하고, 변환, 나를 위해 편리한이었다.
다음은 영업 이익이 질문 꽤 무엇 아니지만, 그것은 올바른 열 이름을 가진 SQL 테이블에 열 머리글로 CSV (실제로 탭-SV)과 같은 스프레드 시트를 수출하고, 변환, 나를 위해 편리한이었다.
IF OBJECT_ID('dbo.uspDumpMultilinesWithHeaderIntoTable', 'P') IS NOT NULL DROP PROCEDURE dbo.uspDumpMultilinesWithHeaderIntoTable; GO CREATE PROCEDURE dbo.uspDumpMultilinesWithHeaderIntoTable @TableName VARCHAR(32), @Multilines VARCHAR(MAX) AS SET NOCOUNT ON IF OBJECT_ID('tempdb..#RawData') IS NOT NULL DROP TABLE #RawData IF OBJECT_ID('tempdb..#RawDataColumnnames') IS NOT NULL DROP TABLE #RawDataColumnnames DECLARE @RowDelim VARCHAR(9) = '
' DECLARE @ColDelim VARCHAR(9) = CHAR(9) DECLARE @MultilinesSafe VARCHAR(MAX) DECLARE @MultilinesXml XML--VARCHAR(MAX) DECLARE @ColumnNamesAsString VARCHAR(4000) DECLARE @SQL NVARCHAR(4000), @ParamDef NVARCHAR(4000) SET @MultilinesSafe = REPLACE(@Multilines, CHAR(10), '') -- replace LF SET @MultilinesSafe = (SELECT REPLACE(@MultilinesSafe, CHAR(10), '') FOR XML PATH('')) -- escape any XML confusion SET @MultilinesSafe = '<rows><row first="1"><cols><col first="1">' + REPLACE(REPLACE(@MultilinesSafe, @RowDelim, '</col></cols></row><row first="0"><cols><col first="0">'), @ColDelim, '</col><col>') + '</col></cols></row></rows>' SET @MultilinesXml = @MultilinesSafe --PRINT CAST(@MultilinesXml AS VARCHAR(MAX)) -- extract Column names SELECT IDENTITY(INT, 1, 1) AS ID, t.n.query('.').value('.', 'VARCHAR(4000)') AS ColName INTO #RawDataColumnnames FROM @MultilinesXml.nodes('/rows/row[@first="1"]/cols/col') AS t(n) -- just first row ALTER TABLE #RawDataColumnnames ADD CONSTRAINT [PK_#RawDataColumnnames] PRIMARY KEY CLUSTERED(ID) -- now tidy any strange characters in column name UPDATE T SET ColName = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(ColName, '.', '_'), ' ', '_'), '[', ''), ']', ''), '.', ''), '$', '') FROM #RawDataColumnnames T -- create output table SET @SQL = 'IF OBJECT_ID(''' + @TableName + ''') IS NOT NULL DROP TABLE ' + @TableName --PRINT 'TableDelete SQL=' + @SQL EXEC sp_executesql @SQL SET @SQL = 'CREATE TABLE ' + @TableName + '(' SELECT @SQL = @SQL + CASE T.ID WHEN 1 THEN '' ELSE ', ' END + CHAR(13) + '['+ T.ColName + '] VARCHAR(4000) NULL' FROM #RawDataColumnnames T ORDER BY ID SET @SQL = @SQL + ')' --PRINT 'TableCreate SQL=' + @SQL EXEC sp_executesql @SQL -- insert data into output table SET @SQL = 'INSERT INTO ' + @TableName + ' SELECT ' SELECT @SQL = @SQL + CONCAT(CHAR(13) , CASE T.ID WHEN 1 THEN ' ' ELSE ',' END , ' t.n.value(''col[', T.ID, ']'', ''VARCHAR(4000)'') AS TheCol', T.ID) FROM #RawDataColumnnames T ORDER BY ID SET @SQL = @SQL + CONCAT(CHAR(13), 'FROM @TheXml.nodes(''/rows/row[@first="0"]/cols'') as t(n)') --PRINT 'Insert SQL=' + @SQL SET @ParamDef = N'@TheXml XML' EXEC sp_ExecuteSql @SQL, @ParamDef, @TheXml=@MultilinesXml GO
예 변환 (공간 탭 참고!)
EXEC dbo.uspDumpMultilinesWithHeaderIntoTable 'Deleteme', 'Left Centre Right A B C D E F G H I'
(비아 'deleteme FROM SELECT *')에
Left Centre Right A B C D E F G H I
이되지 효율 연습으로 만 작업을 수행하려면 작성, 실용적인 코드합니다.
IF OBJECT_ID('dbo.uspDumpMultilinesWithHeaderIntoTable', 'P') IS NOT NULL DROP PROCEDURE dbo.uspDumpMultilinesWithHeaderIntoTable; GO CREATE PROCEDURE dbo.uspDumpMultilinesWithHeaderIntoTable @TableName VARCHAR(127), @Multilines VARCHAR(MAX), @ColDelimDefault VARCHAR(9) = NULL, @Debug BIT = NULL AS SET NOCOUNT ON IF OBJECT_ID('tempdb..#RawData') IS NOT NULL DROP TABLE #RawData IF OBJECT_ID('tempdb..#RawDataColumnnames') IS NOT NULL DROP TABLE #RawDataColumnnames DECLARE @Msg VARCHAR(4000) DECLARE @PosCr INT, @PosNl INT, @TypeRowDelim VARCHAR(20) -- work out type of row delimiter(s) SET @PosCr = CHARINDEX(CHAR(13), @Multilines) SET @PosNl = CHARINDEX(CHAR(10), @Multilines) SET @TypeRowDelim = CASE WHEN @PosCr = @PosNl + 1 THEN 'NL_CR' WHEN @PosCr = @PosNl - 1 THEN 'CR_NL' WHEN @PosCr = 0 AND @PosNl > 0 THEN 'NL' WHEN @PosCr > 0 AND @PosNl = 0 THEN 'CR' ELSE CONCAT('? CR@', @PosCr, ', NL@', @PosNl, ' is unexpected') END -- CR(x0d) is a 'good' row delimiter - make the data fit DECLARE @RowDelim VARCHAR(9) DECLARE @MultilinesSafe VARCHAR(MAX) IF @TypeRowDelim = 'CR_NL' OR @TypeRowDelim = 'NL_CR' BEGIN SET @RowDelim = '
' SET @MultilinesSafe = REPLACE(@Multilines, CHAR(10), '') -- strip LF SET @MultilinesSafe = (SELECT @MultilinesSafe FOR XML PATH('')) -- escape any XML confusion END ELSE IF @TypeRowDelim = 'CR' BEGIN SET @RowDelim = '
' SET @MultilinesSafe = @Multilines SET @MultilinesSafe = (SELECT @MultilinesSafe FOR XML PATH('')) -- escape any XML confusion END ELSE IF @TypeRowDelim = 'NL' BEGIN SET @RowDelim = '
' SET @MultilinesSafe = REPLACE(@Multilines, CHAR(10), CHAR(13)) -- change LF to CR SET @MultilinesSafe = (SELECT @MultilinesSafe FOR XML PATH('')) -- escape any XML confusion END ELSE RAISERROR(@TypeRowDelim , 10, 10) DECLARE @ColDelim VARCHAR(9) = COALESCE(@ColDelimDefault, CHAR(9)) DECLARE @MultilinesXml XML DECLARE @ColumnNamesAsString VARCHAR(4000) DECLARE @SQL NVARCHAR(4000), @ParamDef NVARCHAR(4000) IF @Debug = 1 BEGIN SET @Msg = CONCAT('TN=<', @TableName, '>, TypeRowDelim=<', @TypeRowDelim, '>, RowDelim(XML)=<', @RowDelim, '>, ColDelim=<', @ColDelim, '>, LEN(@Multilines)=', LEN(@Multilines)) PRINT @Msg END SET @MultilinesSafe = '<rows><row first="1"><cols><col first="1">' + REPLACE(REPLACE(@MultilinesSafe, @RowDelim, '</col></cols></row><row first="0"><cols><col first="0">'), @ColDelim, '</col><col>') + '</col></cols></row></rows>' SET @MultilinesXml = @MultilinesSafe --IF @Debug = 1 PRINT CAST(@MultilinesXml AS VARCHAR(MAX)) -- extract Column names SELECT IDENTITY(INT, 1, 1) AS ID, t.n.query('.').value('.', 'VARCHAR(4000)') AS ColName INTO #RawDataColumnnames FROM @MultilinesXml.nodes('/rows/row[@first="1"]/cols/col') AS t(n) -- just first row ALTER TABLE #RawDataColumnnames ADD CONSTRAINT [PK_#RawDataColumnnames] PRIMARY KEY CLUSTERED(ID) -- now tidy any strange characters in column name UPDATE T SET ColName = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(ColName, '.', '_'), ' ', '_'), '[', ''), ']', ''), '.', ''), '$', '') FROM #RawDataColumnnames T -- now fix any empty column names UPDATE T SET ColName = CONCAT('_Col_', ID, '_') FROM #RawDataColumnnames T WHERE ColName = '' IF @Debug = 1 BEGIN SET @Msg = CONCAT('#Cols(FromHdr)=', (SELECT COUNT(*) FROM #RawDataColumnnames) ) PRINT @Msg END -- create output table SET @SQL = 'IF OBJECT_ID(''' + @TableName + ''') IS NOT NULL DROP TABLE ' + @TableName --PRINT 'TableDelete SQL=' + @SQL EXEC sp_executesql @SQL SET @SQL = 'CREATE TABLE ' + @TableName + '(' SET @SQL = @SQL + '[_Row_PK_] INT IDENTITY(1,1) PRIMARY KEY,' -- PK SELECT @SQL = @SQL + CASE T.ID WHEN 1 THEN '' ELSE ', ' END + CHAR(13) + '['+ T.ColName + '] VARCHAR(4000) NULL' FROM #RawDataColumnnames T ORDER BY ID SET @SQL = @SQL + ')' --PRINT 'TableCreate SQL=' + @SQL EXEC sp_executesql @SQL -- insert data into output table SET @SQL = 'INSERT INTO ' + @TableName + ' SELECT ' SELECT @SQL = @SQL + CONCAT(CHAR(13) , CASE T.ID WHEN 1 THEN ' ' ELSE ',' END , ' t.n.value(''col[', T.ID, ']'', ''VARCHAR(4000)'') AS TheCol', T.ID) FROM #RawDataColumnnames T ORDER BY ID SET @SQL = @SQL + CONCAT(CHAR(13), 'FROM @TheXml.nodes(''/rows/row[@first="0"]/cols'') as t(n)') --PRINT 'Insert SQL=' + @SQL SET @ParamDef = N'@TheXml XML' EXEC sp_ExecuteSql @SQL, @ParamDef, @TheXml=@MultilinesXml GO
이것을 실행
EXEC dbo.uspDumpMultilinesWithHeaderIntoTable 'Deleteme', 'Left Right A B C D E F G H I'
의 결과
_Row_PK_ Left _Col_2_ Right 1 A B C 2 D E F 3 G H I
from https://stackoverflow.com/questions/39752188/split-string-into-table-given-row-delimiter-and-column-delimiter-in-sql-server by cc-by-sa and MIT license
'SQL' 카테고리의 다른 글
[SQL] 방법 최대 절전 모드 HQL에서 오라클의 REGEXP_LIKE를 사용 하는가? (0) | 2020.07.11 |
---|---|
[SQL] LocalDB에 연결하는 IIS (0) | 2020.07.11 |
[SQL] SQL 오라클 LEFT은 가입 및 하위 쿼리 오류 : ORA-00905 : 키워드 누락 (0) | 2020.07.11 |
[SQL] SQL 서버 2005에 XML 컨텐츠를 저장하는 데 실패 시도 (인코딩 문제) (0) | 2020.07.11 |
[SQL] 최고의 성능을 ANDS에 Where 절의 주문 (0) | 2020.07.11 |