2010-07-08 14 views
16

Używam ogólnego systemu do raportowania, który pobiera dane z widoku bazy danych (SQL Server 2005). W tym widoku musiałem scalić dane z relacji jeden do wielu w jednym wierszu i użyć rozwiązania opisanego przez priyanka.sarkar w tym wątku: Combine multiple results in a subquery into a single comma-separated value. Rozwiązanie wykorzystuje SQLXML do scalania danych (podzapytanie):SQLXML bez kodowania 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 

To działa doskonale (to nie jest nawet, że ciężkie wydajności) z wyjątkiem moich danych teraz dostaje XML zakodowany (& =>& etc.) przez SQLXML -I w ogóle nie chciałem danych XML, użyłem tego jako sztuczki - i ze względu na ogólny system, nie mogę tego zakodować, aby oczyścić dane, aby zakodowane dane trafiały bezpośrednio do raportu. Nie mogę używać procedur przechowywanych w systemie generycznym, więc łączenie CURSOR-a lub COALESCE-ing nie jest tutaj opcją ...

Więc szukam sposobu w T-SQL, który pozwala mi dekodować XML ponownie, a nawet lepiej: unika kodowania SQLXML. Oczywiście mógłbym napisać przechowywane funkcji, która to robi, ale wolałbym wbudowany, bardziej bezpieczny sposób ...

dzięki za pomoc ...

Odpowiedz

5

Jeśli podasz type jako opcja for xml, można użyć zapytania XPath, aby przekonwertować typ XML z powrotem na varchar. Dzięki zmiennej przykład tabeli:

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

Jednym z możliwych rozwiązań jest:

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) 

ten wypisze:

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

Oto alternatywa bez konwersji XML. Ma kwerendę rekurencyjną, więc przebieg wydajności może się różnić. To od 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

Dzięki, uproszczone "(SELECT ... FOR XML PATH (''), typ) .Value ('', 'nvarchar (max)'.)" – Koen

+2

+1 Byłem zdezorientowany przez zastosowanie krzyża i opcje root'a, więc dodałem komentarz @ Koena jako odpowiedź. – dotjoe

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

Ta dyrektywa 'type' zaoszczędziła mi tyle czasu. – Sung

Powiązane problemy