it-roy-ru.com

Как вещи и «для пути XML» работают в Sql Server

Таблица является:

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

Этот запрос работает правильно. Но мне просто нужно объяснение, как это работает, или есть какой-то другой или короткий способ сделать это.

Я очень смущен, чтобы понять это.

275
Puneet Chawla

Вот как это работает:

1. Получить строку XML-элемента с помощью FOR XML

Добавление FOR XML PATH в конец запроса позволяет выводить результаты запроса в виде элементов XML с именем элемента, содержащимся в аргументе PATH. Например, если мы запустим следующий оператор:

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

. Присоединитесь к 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 |
-----------------------------------

Надеюсь это поможет!

543
FutbolFan

Эта статья охватывает различные способы объединения строк в SQL, включая улучшенную версию вашего кода, которая не кодирует XML-данные сцепленных значений.

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,...'

На первый взгляд это выглядит довольно запутанно, но, как и в некоторых других вариантах, имеет тенденцию работать довольно хорошо.

62
Richard Deeming

режим PATH используется при генерации XML из запроса SELECT

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: если вы укажете строку нулевой длины, элемент переноса не будет создан.

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 мы группируем дату по идентификатору.

STUFF (source_string, start, length, add_string) Параметры или Аргументы source_string Исходная строка, которую нужно изменить. Start Позиция в source_string для удаления символов длины и затем вставки add_string. Length Количество символов, которые необходимо удалить из строки source_string. Add_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 |
-----------------------------------
32
Neha Chopra

В базе данных SQL Azure и 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, как мне показалось, что я увидел это на потенциальной функции, которая должна была быть включена. Либо я вспомнил, что неправильно, либо что-то изменилось, спасибо за предложенную редакцию, исправляющую версию. Кроме того, это произвело большое впечатление на меня, и он не был полностью осведомлен о процессе многоэтапной проверки, которая просто подтянула меня к окончательному варианту.

16
Brian Jorden

В for xml path, если мы определим какое-либо значение, например [ for xml path('ENVLOPE') ], эти теги будут добавлены с каждой строкой:

<ENVLOPE>
</ENVLOPE>
4
vikas
SELECT ID, 
    abc = STUFF(
                 (SELECT ',' + name FROM temp1 FOR XML PATH ('')), 1, 1, ''
               ) 
FROM temp1 GROUP BY id

Здесь в приведенном выше запросе STUFF функция используется только для удаления первой запятой (,) из сгенерированной xml-строки (,aaa,bbb,ccc,ddd,eee), после чего она станет (aaa,bbb,ccc,ddd,eee).

И FOR XML PATH('') просто преобразует данные столбца в строку (,aaa,bbb,ccc,ddd,eee), но в ПУТЬ мы передаем '', поэтому он не будет создавать тег XML.

И в конце мы сгруппировали записи, используя столбец ID.

2
Mahendra Singh Dhami
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
Omkar Naik

Я сделал отладку и, наконец, вернул свой "заполненный" запрос на него, это нормальный способ.

Просто

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

дает мне содержимое таблицы для записи в таблицу журнала из отлаженного триггера.

1
SlavaTT