Как получить индекс / положение узлов XML - PullRequest
0 голосов
/ 05 июля 2018

Я пытаюсь получить индексы узлов xml при выборе значений с помощью sql:

Это код:

declare @myxml xml ='
<Departments>
    <Department>
        <Employee>
            A
        </Employee>
        <Employee>
            B
        </Employee>
    </Department>

    <Department>
        <Employee>
            C
        </Employee>
        <Employee>
            D
        </Employee>
    </Department>
</Departments>'

Select Emp = m.value('.','varchar(30)') 

from @myxml.nodes('Departments/Department/Employee') X(m)

Вывод вышеуказанного запроса:

Emp 
A
B
C
D

Ожидаемый результат:

Emp  Department_Index

A         1
B         1
C         2
D         2

т.е. Я хочу, чтобы индекс отдела соответствовал каждому сотруднику отдела. здесь сотрудники A и B относятся к 1-му отделу отделов, а сотрудники C и D относятся ко 2-му отделу отделов.

Итак, я хочу, чтобы это соединяло сложные дочерние XML-объекты с родительскими, где нет уникального ключа.

1 Ответ

0 голосов
/ 05 июля 2018

Это решение, в котором я бы сказал, что оно будет работать во всех сценариях - хотя не гарантируется, что число, связанное с <Department> узлом через ROW_NUMBER(), будет отражать его реальное положение в любой ситуации ( см. примечания и ссылки ниже):

declare @myxml xml ='
<Departments>
    <Department>
        <Employee>
            A
        </Employee>
        <Employee>
            B
        </Employee>
    </Department>

    <Department>
        <Employee>
            C
        </Employee>
        <Employee>
            D
        </Employee>
    </Department>
</Departments>';

- Запрос будет использовать CTE для привязки чисел к первому уровню и передачи внутреннего узла в целом . Конечный SELECT будет использовать переданный узел <Department> и выберет его сотрудников:

WITH NumberedDepartment AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS DepInx
          ,d.query(N'.') AS DepartmentNode
    FROM @myxml.nodes(N'/Departments/Department') AS A(d)
)
SELECT DepInx 
      ,e.value(N'text()[1]','nvarchar(max)') AS Employee
FROM NumberedDepartment
CROSS APPLY DepartmentNode.nodes(N'Department/Employee') AS B(e);

Если вы хотите прочитать о гарантии порядка сортировки , вы могли бы прочитать эту ветку . Особенно читабельны: В чате ниже Джон Каппеллеттис отвечает . Там я предоставляю подход, использующий XQuery, и еще один подход, использующий таблицу подсчета / числа, чтобы выбрать элементы в их положении. Но это довольно сложно и медленно.

ОБНОВЛЕНИЕ: подход с гарантированным порядком сортировки

Этот подход позволит создать таблицу учета на лету . Если у вас есть таблица чисел, это было бы еще лучше ...

Предложение TOP ограничит это число фактическим числом <Department> узлов. Обязательно используйте исходную таблицу (я использую master..spt_values), в которой есть как минимум столько строк, сколько вам может понадобиться.

При первом применении будет использоваться .query() вместе с sql:column(), чтобы получить правильный узел отдела для каждого номера. Второе заявление будет читать соответствующие сотрудники.

WITH Tally(Nmbr) AS
(
    SELECT TOP (SELECT @myxml.value(N'count(/Departments/Department)','int'))
           ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) 
    FROM master..spt_values --just a pre-filled table with many rows
)
SELECT Nmbr
      ,e.value(N'text()[1]','nvarchar(max)') AS Employee
FROM Tally
OUTER APPLY(SELECT @myxml.query(N'/Departments/Department[sql:column("Nmbr")]')) AS A(ds)
OUTER APPLY ds.nodes(N'Department/Employee') AS B(e);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...