SQL Server에서 Stuff 및 'For Xml Path'작동 방식


367

테이블은 :

+----+------+
| Id | Name |
+----+------+    
| 1  | aaa  |
| 1  | bbb  |
| 1  | ccc  |
| 1  | ddd  |
| 1  | eee  |
+----+------+

필요한 출력 :

+----+---------------------+
| Id |        abc          |
+----+---------------------+ 
|  1 | aaa,bbb,ccc,ddd,eee |
+----+---------------------+

질문:

SELECT ID, 
    abc = STUFF(
                 (SELECT ',' + name FROM temp1 FOR XML PATH ('')), 1, 1, ''
               ) 
FROM temp1 GROUP BY id

이 쿼리는 제대로 작동합니다. 그러나 나는 그것이 어떻게 작동하는지 또는 이것을 할 수있는 다른 방법이나 짧은 방법이 있는지 설명이 필요합니다.

나는 이것을 이해하기가 매우 혼란스러워지고 있습니다.



1
SqlFiddle 페이지 를 만들어 실제로 실제로 작동하는지 확인했습니다. 그것이 다른 사람들을 돕기를 바랍니다.
— Sabuncu

1
^ 아마 ID다른 엔터티의 다른 테이블에서 고유 한 것일 수 있으며이 테이블은 그들에 속한 것을 저장하고 있습니다.
— Nick Rolando

일부 행에 다른 ID가 있으면이 쿼리가 작동하지 않습니다. 예를 들어 'ddd'와 'eee'에 ID가 2 인 경우
— KevinVictor

10
이 페이지를 매월 방문하여 내가 어디로 잘못 갔는지 볼 시간입니다.
— Taylor Ackley

답변:


683

작동 방식은 다음과 같습니다.

1. FOR XML로 XML 요소 문자열 가져 오기

쿼리 끝에 FOR XML PATH를 추가하면 PATH 인수에 포함 된 요소 이름과 함께 쿼리 결과를 XML 요소로 출력 할 수 있습니다. 예를 들어, 다음 명령문을 실행하는 경우 :

SELECT ',' + name 
              FROM temp1
              FOR XML PATH ('')

빈 문자열 (FOR XML PATH ( ''))을 전달하면 다음과 같은 결과가 나타납니다.

,aaa,bbb,ccc,ddd,eee

2. STUFF로 선행 쉼표를 제거하십시오

STUFF 문은 문자 그대로 한 문자열을 다른 문자열로 "붙여서"첫 번째 문자열 내의 문자를 대체하지만 결과 값 목록에서 첫 번째 문자를 제거하는 데 간단히 사용합니다.

SELECT abc = STUFF((
            SELECT ',' + NAME
            FROM temp1
            FOR XML PATH('')
            ), 1, 1, '')
FROM temp1

매개 변수 STUFF는 다음 과 같습니다.

  • "채워질"문자열 (이 경우 앞에 쉼표가있는 전체 이름 목록)
  • 문자 삭제 및 삽입을 시작하는 위치 (1, 빈 문자열에 채워 넣음)
  • 삭제할 문자 수 (1, 선행 쉼표)

그래서 우리는 다음과 같이 끝납니다.

aaa,bbb,ccc,ddd,eee

3. 전체 목록을 얻으려면 id에 가입하십시오.

다음으로 임시 테이블의 id 목록에서 이것을 결합하여 이름이있는 ID 목록을 얻습니다.

SELECT ID,  abc = STUFF(
             (SELECT ',' + name 
              FROM temp1 t1
              WHERE t1.id = t2.id
              FOR XML PATH (''))
             , 1, 1, '') from temp1 t2
group by id;

결과는 다음과 같습니다.

-----------------------------------
| Id        | Name                |
|---------------------------------|
| 1         | aaa,bbb,ccc,ddd,eee |
-----------------------------------

도움이 되었기를 바랍니다!


57
Microsoft의 문서 팀 (있는 경우)을 위해 일해야합니다
— Fandango68

55
@ Fandango68, @ FutbolFan-그는 Microsoft의 문서 팀에서 일할 수 없습니다. 그의 설명은 너무 분명하고 직접적입니다. ;-)
— Chris

1
@ChrisProsser 동의합니다. Oracle은 LISTAGGOracle 11gR2에 기능 을 도입하여 Microsoft보다 앞서 있습니다. 나는 이것을 대신 사용해야하는 날에는 그 기능을 그리워합니다. techonthenet.com/oracle/functions/listagg.php
— FutbolFan

2
안녕하세요. 1 단계에서 수행하는 경우 : SELECT name FROM temp1 FOR XML PATH ( '') ... <name> aaa </ name> <name> bbb </ name> ... etc ...를 얻습니다. 처음에는 이것을 인식하지 못합니다 ... SELECT ''+ name ... etc ...로 변경하면 태그가 제거됩니다.
— KevinVictor

1
@ChrisProsser-Sybase ASA는 list수십 년 동안 기능을 해왔습니다. 불행히도 Microsoft는 Sybase의 ASE를 기반으로 한 SQLServer를 대신 사용했으며 작년까지 목록 기능을 사용하지 않았습니다. 동의합니다-마음이 번쩍입니다. 그리고 그들은 그렇게합니다 string_agg. 나는 list꽤 명백 하다고 생각했을 것 입니다.
— youcantryreachingme

75

이 기사 에서는 연결된 값을 XML로 인코딩하지 않는 코드의 개선 된 버전을 포함하여 SQL에서 문자열을 연결하는 다양한 방법을 다룹니다.

SELECT ID, abc = STUFF
(
    (
        SELECT ',' + name
        FROM temp1 As T2
        -- You only want to combine rows for a single ID here:
        WHERE T2.ID = T1.ID
        ORDER BY name
        FOR XML PATH (''), TYPE
    ).value('.', 'varchar(max)')
, 1, 1, '')
FROM temp1 As T1
GROUP BY id

무슨 일이 일어나고 있는지 이해하려면 내부 쿼리로 시작하십시오.

SELECT ',' + name
FROM temp1 As T2
WHERE T2.ID = 42 -- Pick a random ID from the table
ORDER BY name
FOR XML PATH (''), TYPE

을 지정하기 때문에 FOR XML모든 행을 나타내는 XML 조각이 포함 된 단일 행을 얻게됩니다.

첫 번째 열에 열 별칭을 지정하지 않았기 때문에 각 행은 이름 뒤에 괄호 안에 지정된 이름을 가진 XML 요소로 래핑됩니다 FOR XML PATH. 예 FOR XML PATH ('X')를 들어을 가진 경우 다음 과 같은 XML 문서가 표시됩니다.

<X>,aaa</X>
<X>,bbb</X>
...

그러나 요소 이름을 지정하지 않았으므로 값 목록 만 얻습니다.

,aaa,bbb,...

는 .value('.', 'varchar(max)')간단히 "특별한"문자를 XML은 인코딩없이, 결과 XML 조각에서 값을 검색합니다. 이제 다음과 같은 문자열이 있습니다.

',aaa,bbb,...'

이 STUFF함수는 선행 쉼표를 제거하여 다음과 같은 최종 결과를 제공합니다.

'aaa,bbb,...'

언뜻보기에는 혼란스러워 보이지만 다른 옵션과 비교할 때 성능이 뛰어납니다.


2
쿼리에서 유형을 사용하는 것은 무엇입니까? 나는 그것을 정의하기 위해 그것을 생각한다 .XML 경로의 결과는 가치가 저장 될 것이다.
— 푸네 Chawla

8
@PuneetChawla는 : 지시어는 SQL이 사용하여 데이터를 반환하도록 지시 유형입니다. 그것이 없으면 데이터는로 반환됩니다 . 열에 특수 문자가있는 경우 XML 인코딩 문제를 피하기 위해 여기에서 사용 됩니다. TYPExmlnvarchar(max)name
— Richard Deeming

2
@barlop : SimpleTalk 기사에서 설명 하는 것처럼 TYPEand 를 삭제 .value('.', 'varchar(max)')하면 결과에 XML 인코딩 엔터티가 생길 수 있습니다.
— Richard Deeming

1
@RichardDeeming 데이터에 꺾쇠 괄호가 있거나 포함되어 있는지 여부
— barlop

1
그러나 요소 이름을 지정하지 않았으므로 값 목록 만 얻습니다 . 이것은 내가 놓친 통찰력입니다. 감사합니다.
— 아담

44

PATH 모드는 SELECT 쿼리에서 XML을 생성하는 데 사용됩니다

1. SELECT   
       ID,  
       Name  
FROM temp1
FOR XML PATH;  

Ouput:
<row>
<ID>1</ID>
<Name>aaa</Name>
</row>

<row>
<ID>1</ID>
<Name>bbb</Name>
</row>

<row>
<ID>1</ID>
<Name>ccc</Name>
</row>

<row>
<ID>1</ID>
<Name>ddd</Name>
</row>

<row>
<ID>1</ID>
<Name>eee</Name>
</row>

출력은 결과 행 집합의 각 열 값이 행 요소로 래핑되는 요소 중심 XML입니다. SELECT 절은 컬럼 이름에 대한 별명을 지정하지 않으므로 생성 된 하위 요소 이름은 SELECT 절의 해당 컬럼 이름과 동일합니다.

행 집합의 각 행에 대해 태그가 추가됩니다.

2.
SELECT   
       ID,  
       Name  
FROM temp1
FOR XML PATH('');

Ouput:
<ID>1</ID>
<Name>aaa</Name>
<ID>1</ID>
<Name>bbb</Name>
<ID>1</ID>
<Name>ccc</Name>
<ID>1</ID>
<Name>ddd</Name>
<ID>1</ID>
<Name>eee</Name>

2 단계 : 길이가 0 인 문자열을 지정하면 줄 바꿈 요소가 생성되지 않습니다.

3. 

    SELECT   

           Name  
    FROM temp1
    FOR XML PATH('');

    Ouput:
    <Name>aaa</Name>
    <Name>bbb</Name>
    <Name>ccc</Name>
    <Name>ddd</Name>
    <Name>eee</Name>

4. SELECT   
        ',' +Name  
FROM temp1
FOR XML PATH('')

Ouput:
,aaa,bbb,ccc,ddd,eee

4 단계에서 값을 연결합니다.

5. SELECT ID,
    abc = (SELECT   
            ',' +Name  
    FROM temp1
    FOR XML PATH('') )
FROM temp1

Ouput:
1   ,aaa,bbb,ccc,ddd,eee
1   ,aaa,bbb,ccc,ddd,eee
1   ,aaa,bbb,ccc,ddd,eee
1   ,aaa,bbb,ccc,ddd,eee
1   ,aaa,bbb,ccc,ddd,eee


6. SELECT ID,
    abc = (SELECT   
            ',' +Name  
    FROM temp1
    FOR XML PATH('') )
FROM temp1 GROUP by iD

Ouput:
ID  abc
1   ,aaa,bbb,ccc,ddd,eee

6 단계에서 날짜별로 ID를 그룹화합니다.

STUFF (source_string, start, length, add_string) 매개 변수 또는 인수 source_string 수정할 소스 문자열. start 길이 문자를 삭제하고 add_string을 삽입 할 source_string의 위치입니다. length source_string에서 삭제할 문자 수입니다. add_string 시작 위치에서 source_string에 삽입 할 문자 순서.

SELECT ID,
    abc = 
    STUFF (
        (SELECT   
                ',' +Name  
        FROM temp1
        FOR XML PATH('')), 1, 1, ''
    )
FROM temp1 GROUP by iD

Output:
-----------------------------------
| Id        | Name                |
|---------------------------------|
| 1         | aaa,bbb,ccc,ddd,eee |
-----------------------------------

1
"4 단계에서 값을 연결합니다."라고 쓴다. 그러나 xml 경로 이후 ','와 결합 된 열로 지정된 이유 / 어떻게 ('')연결이 발생 하는지는 확실하지 않습니다.
— barlop

4 단계에서 문자열 조작을 수행하면이 경우 공백 ( '') 인 지정된 랩핑 요소를 사용합니다.
— vCillusion

1
포인트 4와 왜 <이름>이 (가) 사라지는 지 궁금해하는 사람. 쉼표로 이름을 연결 한 후에는 더 이상 열이 아니라 값만 있기 때문에 SQL Server는 어떤 XML 태그 이름을 사용해야하는지 알 수 없기 때문입니다. 예를 들어이 쿼리 SELECT 'a' FROM some_table FOR XML PATH('')는 다음을 생성 'aaaaaaa'합니다.. 그러나 열 이름을 지정 SELECT 'a' AS Col FROM some_table FOR XML PATH('')하면 다음과 같은 결과가 나타납니다.<Col>a</Col><Col>a</Col><Col>a</Col>
— anth

23

이 정확한 시나리오를 처리하기 위해 Azure SQL Database 및 SQL Server (2017부터 시작)에 새로운 기능이 있습니다. 나는 이것이 XML / STUFF 방법으로 달성하려는 것에 대한 공식적인 공식 방법이 될 것이라고 생각합니다. 예:

select id, STRING_AGG(name, ',') as abc
from temp1
group by id

STRING_AGG- https: //msdn.microsoft.com/en-us/library/mt790580.aspx

편집 : 처음에 이것을 게시했을 때 SQL Server 2016에 대해 언급했을 때 포함 될 잠재적 기능에 대해 생각했습니다. 수정 된 수정 버전 덕분에 잘못되었거나 변경된 것이 있음을 기억했습니다. 또한 최종 옵션으로 나를 데려 간 다단계 검토 프로세스를 완전히 인식하지 못했습니다.


3
STRING_AGG는 SQL Server 2016에 없습니다. "vNext"에 있다고합니다.
— N8allan

죄송합니다. @lostmylogin에서 수정 한 내용을 덮어 쓰려고하지 않았습니다. 죄송합니다. 그게 바로 수정 편집을 진행 한 사람입니다.
— Brian Jorden

5

에서 for xml path같은 값을 정의하면 [ for xml path('ENVLOPE') ]이 태그가 각 행에 추가됩니다.

<ENVLOPE>
</ENVLOPE>

2
SELECT ID, 
    abc = STUFF(
                 (SELECT ',' + name FROM temp1 FOR XML PATH ('')), 1, 1, ''
               ) 
FROM temp1 GROUP BY id

다음은 위의 질의에 물건 기능은 첫 번째 쉼표 제거하는 데 사용됩니다 (,)생성 된 XML 문자열에서를 (,aaa,bbb,ccc,ddd,eee)다음이 될 것입니다(aaa,bbb,ccc,ddd,eee) .

그리고 FOR XML PATH('')단순히 열 데이터를 (,aaa,bbb,ccc,ddd,eee)문자열 로 변환 하지만 PATH 에서는 ''을 전달하므로 XML 태그를 만들지 않습니다.

그리고 마지막으로 ID 열을 사용하여 레코드를 그룹화했습니다 .


2

나는 디버깅을하고 마침내 '채워진'쿼리를 정상적인 방법으로 반환했습니다.

간단히

select * from myTable for xml path('myTable')

디버그하는 트리거에서 로그 테이블에 쓸 테이블의 내용을 제공합니다.


1
Declare @Temp As Table (Id Int,Name Varchar(100))
Insert Into @Temp values(1,'A'),(1,'B'),(1,'C'),(2,'D'),(2,'E'),(3,'F'),(3,'G'),(3,'H'),(4,'I'),(5,'J'),(5,'K')
Select X.ID,
stuff((Select ','+ Z.Name from @Temp Z Where X.Id =Z.Id For XML Path('')),1,1,'')
from @Temp X
Group by X.ID

-1

STUFF ((SELECT SELECT ','+ CAST (T.ID) FROM T TIDID = 1 FOR XML PATH ( '')), 1,1, '') AS 이름


-3

with 절을 자주 사용하고 있습니다.

SELECT 
TapuAda=STUFF(( 
SELECT ','+TBL.TapuAda FROM (
SELECT TapuAda FROM T_GayrimenkulDetay AS GD 
INNER JOIN dbo.T_AktiviteGayrimenkul AS AG ON  D.GayrimenkulID=AG.GayrimenkulID WHERE 
AG.AktiviteID=262
) AS TBL FOR XML PATH ('')
),1,1,'')

2
나는 이것이 어떻게 대답인지 알지 못합니다. 몇 가지 설명으로 던질 수 있습니까?
— Gar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.