[sql] SQL Server에서 Stuff 및 ‘For Xml Path’작동 방식

테이블은 :

+----+------+
| 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. 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 |
-----------------------------------

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


답변

이 기사 에서는 연결된 값을 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,...'

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


답변

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 |
-----------------------------------


답변

이 정확한 시나리오를 처리하기 위해 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에 대해 언급했을 때 포함 될 잠재적 기능에 대해 생각했습니다. 수정 된 수정 버전 덕분에 잘못되었거나 변경된 것이 있음을 기억했습니다. 또한 최종 옵션으로 나를 데려 간 다단계 검토 프로세스를 완전히 인식하지 못했습니다.


답변

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

<ENVLOPE>
</ENVLOPE>


답변

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 열을 사용하여 레코드를 그룹화했습니다 .


답변

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

간단히

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

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