SQL Server에서 문자열을 연결하여 그룹을 사용하는 방법은 무엇입니까?
-
07-07-2019 - |
문제
어떻게 내가 가질까:
id Name Value
1 A 4
1 B 8
2 C 9
에게
id Column
1 A:4, B:8
2 C:9
해결책
커서, 루프 또는 사용자 정의 기능이 필요하지 않습니다..
XML 및 경로를 위해 창의력을 발휘해야합니다.
참고 :이 솔루션은 SQL 2005 이상에서만 작동합니다. 원래 질문은 사용중인 버전을 지정하지 않았습니다.
CREATE TABLE #YourTable ([ID] INT, [Name] CHAR(1), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'A',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'B',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
SELECT
[ID],
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
다른 팁
SQL Server 2017 또는 SQL Server VNEXT 인 경우 SQL Azure는 다음과 같이 String_agg를 사용할 수 있습니다.
select id, string_agg(concat(name, ':', [value]), ', ')
from #YourTable
group by id
XML 경로를 사용하면 예상대로 완벽하게 연결되지 않습니다 ... "&"& "로 대체하고 <" and ">
... 어쩌면 몇 가지 다른 것들이 확실하지 않습니다 ...하지만 당신은 이것을 시도 할 수 있습니다
나는 이것에 대한 해결 방법을 발견했다 ... 당신은 교체해야한다 :
FOR XML PATH('')
)
와 함께:
FOR XML PATH(''),TYPE
).value('(./text())[1]','VARCHAR(MAX)')
...또는 NVARCHAR(MAX)
그게 당신이 사용하는 것입니다.
도대체 왜 그렇지 않습니다 SQL
컨텍스트 집계 기능이 있습니까? 이것은 피타입니다.
공백과 특수 XML 문자가 포함 된 문자열로 작업하기 위해 Kevin Fairchild의 제안을 변환하려고 시도했을 때 몇 가지 문제가 발생했습니다.&
, <
, >
) 인코딩 된.
내 코드의 최종 버전 (원래 질문에 응답하지 않지만 누군가에게 유용 할 수 있음)은 다음과 같습니다.
CREATE TABLE #YourTable ([ID] INT, [Name] VARCHAR(MAX), [Value] INT)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'Oranges & Lemons',4)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (1,'1 < 2',8)
INSERT INTO #YourTable ([ID],[Name],[Value]) VALUES (2,'C',9)
SELECT [ID],
STUFF((
SELECT ', ' + CAST([Name] AS VARCHAR(MAX))
FROM #YourTable WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE
/* Use .value to uncomment XML entities e.g. > < etc*/
).value('.','VARCHAR(MAX)')
,1,2,'') as NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
공간을 구분 기자로 사용하고 모든 공간을 쉼표로 교체하는 대신 각 값에 쉼표와 공간을 사전 지불 한 다음 사용합니다. STUFF
처음 두 문자를 제거합니다.
XML 인코딩은 유형 지령.
SQL Server 2005 이상을 사용하는 또 다른 옵션
---- test data
declare @t table (OUTPUTID int, SCHME varchar(10), DESCR varchar(10))
insert @t select 1125439 ,'CKT','Approved'
insert @t select 1125439 ,'RENO','Approved'
insert @t select 1134691 ,'CKT','Approved'
insert @t select 1134691 ,'RENO','Approved'
insert @t select 1134691 ,'pn','Approved'
---- actual query
;with cte(outputid,combined,rn)
as
(
select outputid, SCHME + ' ('+DESCR+')', rn=ROW_NUMBER() over (PARTITION by outputid order by schme, descr)
from @t
)
,cte2(outputid,finalstatus,rn)
as
(
select OUTPUTID, convert(varchar(max),combined), 1 from cte where rn=1
union all
select cte2.outputid, convert(varchar(max),cte2.finalstatus+', '+cte.combined), cte2.rn+1
from cte2
inner join cte on cte.OUTPUTID = cte2.outputid and cte.rn=cte2.rn+1
)
select outputid, MAX(finalstatus) from cte2 group by outputid
SQLCLR 집계를 설치하십시오 http://groupconcat.codeplex.com
그런 다음 이와 같은 코드를 작성하여 요청한 결과를 얻을 수 있습니다.
CREATE TABLE foo
(
id INT,
name CHAR(1),
Value CHAR(1)
);
INSERT INTO dbo.foo
(id, name, Value)
VALUES (1, 'A', '4'),
(1, 'B', '8'),
(2, 'C', '9');
SELECT id,
dbo.GROUP_CONCAT(name + ':' + Value) AS [Column]
FROM dbo.foo
GROUP BY id;
SQL Server 2005 이상 사용자 정의 집계 기능, 연결과 같은 것들을 포함하여 링크 된 기사의 맨 아래에있는 샘플을 참조하십시오.
8 년 후 ... Microsoft SQL Server VNEXT Database Engine은 마침내 그룹화 된 문자열 연결을 직접 지원하도록 Transact-SQL을 향상 시켰습니다. Community Technical Preview 버전 1.0은 String_Agg 함수를 추가하고 CTP 1.1은 String_Agg 함수의 그룹 절을 추가했습니다.
예가 있습니다
Oracle에서는 listagg 집계 기능을 사용할 수 있습니다.
원래 기록
name type
------------
name1 type1
name2 type2
name2 type3
SQL
SELECT name, LISTAGG(type, '; ') WITHIN GROUP(ORDER BY name)
FROM table
GROUP BY name
의 결과
name type
------------
name1 type1
name2 type2; type3
이런 종류의 질문은 여기서 자주 묻는다. 그리고 해결책은 기본 요구 사항에 크게 의존 할 것이다.
https://stackoverflow.com/search?q=sql+pivot
그리고
https://stackoverflow.com/search?q=sql+concatenate
일반적으로 동적 SQL, 사용자 정의 함수 또는 커서 없이는 SQL 전용 방법이 없습니다.
Cade가 말한 것에 추가하기 위해, 이것은 일반적으로 프론트 엔드 디스플레이이므로 처리해야합니다. 파일 내보내기 또는 기타 "SQL 전용"솔루션과 같은 것에 대해 SQL에 100%를 쓰는 것이 더 쉽다는 것을 알고 있지만 대부분의 경우이 연결은 디스플레이 레이어에서 처리해야합니다.
이것은 Kevin Fairchild의 게시물에 추가 된 것입니다 (매우 영리합니다). 나는 그것을 댓글로 추가했을 것이지만 아직 충분한 점이 없다 :)
나는이 아이디어를 내가 작업하고있는 견해를 위해 사용하고 있었지만, 내가 연결하고 있던 항목에는 공간이 포함되어 있습니다. 따라서 공간을 구분자로 사용하지 않도록 코드를 약간 수정했습니다.
멋진 해결 방법 Kevin에 다시 한번 감사드립니다!
CREATE TABLE #YourTable ( [ID] INT, [Name] CHAR(1), [Value] INT )
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (1, 'A', 4)
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (1, 'B', 8)
INSERT INTO #YourTable ([ID], [Name], [Value]) VALUES (2, 'C', 9)
SELECT [ID],
REPLACE(REPLACE(REPLACE(
(SELECT [Name] + ':' + CAST([Value] AS VARCHAR(MAX)) as A
FROM #YourTable
WHERE ( ID = Results.ID )
FOR XML PATH (''))
, '</A><A>', ', ')
,'<A>','')
,'</A>','') AS NameValues
FROM #YourTable Results
GROUP BY ID
DROP TABLE #YourTable
커서가 필요하지 않습니다 ... while 루프만으로도 충분합니다.
------------------------------
-- Setup
------------------------------
DECLARE @Source TABLE
(
id int,
Name varchar(30),
Value int
)
DECLARE @Target TABLE
(
id int,
Result varchar(max)
)
INSERT INTO @Source(id, Name, Value) SELECT 1, 'A', 4
INSERT INTO @Source(id, Name, Value) SELECT 1, 'B', 8
INSERT INTO @Source(id, Name, Value) SELECT 2, 'C', 9
------------------------------
-- Technique
------------------------------
INSERT INTO @Target (id)
SELECT id
FROM @Source
GROUP BY id
DECLARE @id int, @Result varchar(max)
SET @id = (SELECT MIN(id) FROM @Target)
WHILE @id is not null
BEGIN
SET @Result = null
SELECT @Result =
CASE
WHEN @Result is null
THEN ''
ELSE @Result + ', '
END + s.Name + ':' + convert(varchar(30),s.Value)
FROM @Source s
WHERE id = @id
UPDATE @Target
SET Result = @Result
WHERE id = @id
SET @id = (SELECT MIN(id) FROM @Target WHERE @id < id)
END
SELECT *
FROM @Target
매우 간단하게하자 :
SELECT stuff(
(
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
FOR XML PATH('')
)
, 1, 2, '')
이 라인 교체 :
select ', ' + x from (SELECT 'xxx' x union select 'yyyy') tb
쿼리와 함께.
크로스 적용 답변은 보이지 않았으며 XML 추출이 필요하지 않습니다. Kevin Fairchild가 쓴 것과 약간 다른 버전이 있습니다. 더 복잡한 쿼리에서 더 빠르고 사용하기 쉽습니다.
select T.ID
,MAX(X.cl) NameValues
from #YourTable T
CROSS APPLY
(select STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = T.ID)
FOR XML PATH(''))
,1,2,'') [cl]) X
GROUP BY T.ID
Group By가 대부분 하나의 항목을 포함하는 경우 다음과 같은 성능을 향상시킬 수 있습니다.
SELECT
[ID],
CASE WHEN MAX( [Name]) = MIN( [Name]) THEN
MAX( [Name]) NameValues
ELSE
STUFF((
SELECT ', ' + [Name] + ':' + CAST([Value] AS VARCHAR(MAX))
FROM #YourTable
WHERE (ID = Results.ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'') AS NameValues
END
FROM #YourTable Results
GROUP BY ID