Как транспонировать таблицу XML с T-SQL XQuery? - PullRequest
0 голосов
/ 15 января 2010

Допустим, у нас есть следующий XML:

<root>
  <row>
    <column>row 1 col 1</column>
    <column>row 1 col 2</column>
    <column>row 1 col 3</column>
  </row>
  <row>
    <column>row 2 col 1</column>
    <column>row 2 col 2</column>
    <column>row 2 col 3</column>
  </row>
  <row>
    <column>row 3 col 1</column>
    <column>row 3 col 2</column>
    <column>row 3 col 3</column>
  </row>
</root>

Как мне транспонировать это, используя T-SQL XQuery в:

<root>
    <column>
        <row>row 1 col 1</row>
        <row>row 2 col 1</row>
        <row>row 3 col 1</row>
    </column>
    <column>
        <row>row 1 col 2</row>
        <row>row 2 col 2</row>
        <row>row 3 col 2</row>
    </column>
    <column>
        <row>row 1 col 3</row>
        <row>row 2 col 3</row>
        <row>row 3 col 3</row>
    </column>
</root>

Ответы [ 2 ]

1 голос
/ 15 января 2010

Я подозреваю, что может быть действительно хороший подход с использованием PIVOT, но я не знаю этого достаточно хорошо, чтобы сказать наверняка. То, что я предлагаю здесь, работает. Я разделил его на куски для лучшего форматирования и предоставления комментариев:

Для начала давайте захватим данные примера

-- Sample data
DECLARE @x3 xml

SET @x3 = '
<root>
  <row>
    <column>row 1 col 1</column>
    <column>row 1 col 2</column>
    <column>row 1 col 3</column>
  </row>
  <row>
    <column>row 2 col 1</column>
    <column>row 2 col 2</column>
    <column>row 2 col 3</column>
  </row>
  <row>
    <column>row 3 col 1</column>
    <column>row 3 col 2</column>
    <column>row 3 col 3</column>
  </row>
</root>
'

DECLARE @x xml
SET @x = @x3

-- @x is now our input

Теперь актуальный код транспонирования:

Установить размер матрицы:

WITH Size(Size) AS
(
    SELECT CAST(SQRT(COUNT(*)) AS int) 
    FROM @x.nodes('/root/row/column') T(C)
)

Измельчите данные, используйте ROW_NUMBER для захвата индекса (-1 должен сделать его основанным на нуле), и используйте деление по модулю и целому числу для индекса, чтобы обработать новую строку * * номера столбцов:

,Flattened(NewRow, NewCol, Value) AS
(
    SELECT
        -- i/@size as old_r, i % @size as old_c, 
        i % (SELECT TOP 1 Size FROM Size) AS NewRow, 
        i / (SELECT TOP 1 Size FROM Size) AS NewCol, 
        Value
    FROM (
        SELECT
            (ROW_NUMBER() OVER (ORDER BY C)) - 1 AS i, 
            C.value('.', 'nvarchar(100)') AS Value
        FROM @x.nodes('/root/row/column') T(C)
        ) ShreddedInput
)

Имея этот CTE FlattenedInput, все, что нам теперь нужно сделать, - это правильно настроить параметры и структуру запросов FOR XML, и все готово:

SELECT
    (
        SELECT Value 'column'
        FROM
            Flattened t_inner
        WHERE
            t_inner.NewRow = t_outer.NewRow
        FOR XML PATH(''), TYPE
    ) row
FROM
    Flattened t_outer
GROUP BY NewRow
FOR XML PATH(''), ROOT('root')

Пример вывода:

<root>
  <row>
    <column>row 1 col 1</column>
    <column>row 2 col 1</column>
    <column>row 3 col 1</column>
  </row>
  <row>
    <column>row 1 col 2</column>
    <column>row 2 col 2</column>
    <column>row 3 col 2</column>
  </row>
  <row>
    <column>row 1 col 3</column>
    <column>row 2 col 3</column>
    <column>row 3 col 3</column>
  </row>
</root>

Работает с «квадратными» данными любого размера. Обратите внимание на отсутствие проверки работоспособности / обработки ошибок.

0 голосов
/ 14 апреля 2010
SET @x3 = ' 
<root> 
  <row> 
    <column>row 1 col 1</column> 
    <column>row 1 col 2</column> 
    <column>row 1 col 3</column> 
  </row> 
  <row> 
    <column>row 2 col 1</column> 
    <column>row 2 col 2</column> 
    <column>row 2 col 3</column> 
  </row> 
  <row> 
    <column>row 3 col 1</column> 
    <column>row 3 col 2</column> 
    <column>row 3 col 3</column> 
  </row> 
</root> 
' 

select @x3 = replace(@x3,'<row>','<rowtemp>')
select @x3 = replace(@x3,'<column>','<row>')
select @x3 = replace(@x3,'<rowtemp>','<column>')

select @x3
...