2010-07-08 2 views
16

Я использую общую систему для отчетности, которая берет данные из представления базы данных (SQL Server 2005). В этом представлении мне пришлось объединить данные из отношений «один ко многим» в одной строке и использовать решение, описанное priyanka.sarkar в этой теме: Combine multiple results in a subquery into a single comma-separated value. Решение использует SQLXML для объединения данных (подзапрос):SQLXML без кодирования XML?

SELECT STUFF(
    ( SELECT ', ' + Name 
     FROM MyTable _in 
     WHERE _in.ID = _out.ID 
     FOR XML PATH('')),  -- Output multiple rows as one xml type value, 
            -- without xml tags 
    1, 2, '')  -- STUFF: Replace the comma at the beginning with empty string 
FROM MyTable _out 
GROUP BY ID  -- Removes duplicates 

Это прекрасно работает (это даже не тяжелая, что в исполнении), за исключением моих данных в настоящее время получает XML закодированы (& =>& и т.д.) с помощью SQLXML -I не хотел XML-данных, я просто использовал это как трюк, и из-за общей системы я не могу ее кодировать, чтобы очистить его, чтобы закодированные данные попадали прямо в отчет. Я не могу использовать хранимые процедуры с общей системой, поэтому CURSOR-merging или COALESCE-ing здесь не вариант ...

Так что я ищу способ в T-SQL, который позволяет мне декодировать XML снова или даже лучше: избегает кодирования SQLXML. Очевидно, что я мог бы написать хранимую функцию, которая делает это, но я бы предпочел встроенный, более безопасный способ ...

Спасибо за вашу помощь ...

ответ

5

Если указать type в качестве опции for xml, вы можете использовать запрос XPath для преобразования типа XML обратно в varchar. С переменной Пример таблицы:

declare @MyTable table (id int, name varchar(50)) 

insert @MyTable (id, name) select 1, 'Joel & Jeff' 
union all select 1, '<<BIN LADEN>>' 
union all select 2, '&&BUSH&&' 

Одно из возможных решений:

select b.txt.query('root').value('.', 'varchar(max)') 
from (
     select distinct id 
     from @MyTable 
     ) a 
cross apply 
     (
      select CASE ROW_NUMBER() OVER(ORDER BY id) WHEN 1 THEN '' 
         ELSE ', ' END + name 
     from @MyTable 
     where id = a.id 
     order by 
       id 
     for xml path(''), root('root'), type 
     ) b(txt) 

Это будет печатать:

Joel & Jeff, <<BIN LADEN>> 
&&BUSH&& 

Вот альтернатива без преобразования XML. У этого есть рекурсивный запрос, поэтому пробег производительности может отличаться. Это от Quassnoi's blog:

;WITH with_stats(id, name, rn, cnt) AS 
     (
     SELECT id, name, 
       ROW_NUMBER() OVER (PARTITION BY id ORDER BY name), 
       COUNT(*) OVER (PARTITION BY id) 
     FROM @MyTable 
     ), 
     with_concat (id, name, gc, rn, cnt) AS 
     (
     SELECT id, name, 
       CAST(name AS VARCHAR(MAX)), rn, cnt 
     FROM with_stats 
     WHERE rn = 1 
     UNION ALL 
     SELECT with_stats.id, with_stats.name, 
       CAST(with_concat.gc + ', ' + with_stats.name AS VARCHAR(MAX)), 
       with_stats.rn, with_stats.cnt 
     FROM with_concat 
     JOIN with_stats 
     ON  with_stats.id = with_concat.id 
       AND with_stats.rn = with_concat.rn + 1 
     ) 
SELECT id, gc 
FROM with_concat 
WHERE rn = cnt 
OPTION (MAXRECURSION 0) 
+7

Спасибо, я упрощена до "(SELECT ... FOR XML PATH (''), ТИП) .value ('', 'NVARCHAR (макс).)" – Koen

+2

+1 Я был в замешательстве с помощью креста apply и root options, поэтому я добавил комментарий Koen в качестве ответа. – dotjoe

22
(
select ... 
from t 
for xml path(''), type 
).value('.', 'nvarchar(max)') 
+0

Эта директива 'type' спасла меня так много времени. – Sung

Смежные вопросы