Выберите данные XML из столбца, кроме определенных значений T-SQL - PullRequest
1 голос
/ 07 октября 2019

У меня есть следующие таблицы

CREATE TABLE #Table1(XMLValue XML);

INSERT INTO #table1
VALUES
       (
       '<Root>
  <row>
    <VendorPlantName>Plant 1</VendorPlantName>
    <TotalAttainableUnits>3693</TotalAttainableUnits>
  </row>
  <row>
    <VendorPlantName>Plant 2</VendorPlantName>
    <TotalAttainableUnits>69477</TotalAttainableUnits>
  </row>
  <row>
    <VendorPlantName>Plant 3</VendorPlantName>
    <TotalAttainableUnits>573</TotalAttainableUnits>
  </row>
  </Root>'
       );

CREATE TABLE #Table2
(NodeName  NVARCHAR(255)
,NodeValue NVARCHAR(255)
);

INSERT INTO #table2
VALUES
       (
       'VendorPlantName','Plant 2'
       ),
       (
       'VendorPlantName','Plant 1'
       );

С этими двумя таблицами возможно, в разумных пределах, произвести следующий вывод:

<Root>
  <row>
    <VendorPlantName>Plant 3</VendorPlantName>
    <TotalAttainableUnits>573</TotalAttainableUnits>
  </row>
<Root>

Подробнее. XML будет полностью динамичным. Имя узла и значения также будут неизвестны. Можно ли (используя SQL serve 2008 R2) выбирать только те узлы и значения из документа xml, которые не отображаются во второй таблице.

В этом случае используются только возвращаемые имена и значения, которые не имеютбыл замечен раньше. Это было бы намного проще, как динамический SQL-запрос с некоторыми объединениями, но я не могу этого сделать. Любая помощь приветствуется. Даже если ответ - найди другой способ.

1 Ответ

4 голосов
/ 08 октября 2019

Я не знаю, что "XML будет полностью динамичным" действительно означает. Как видно, всегда будет <Root> и множество <row> элементов. Таким образом, динамическая часть - это неизвестный список полей в каждом <row>. Правильно?

Вы можете попробовать кое-что по этому поводу:

DECLARE @tbl1 TABLE(XMLValue XML);

INSERT INTO @tbl1
VALUES
       (
       '<Root>
  <row>
    <VendorPlantName>Plant 1</VendorPlantName>
    <TotalAttainableUnits>3693</TotalAttainableUnits>
  </row>
  <row>
    <VendorPlantName>Plant 2</VendorPlantName>
    <TotalAttainableUnits>69477</TotalAttainableUnits>
  </row>
  <row>
    <VendorPlantName>Plant 3</VendorPlantName>
    <TotalAttainableUnits>573</TotalAttainableUnits>
  </row>
  </Root>'
       );

DECLARE @tbl2 TABLE(NodeName  NVARCHAR(255)
                   ,NodeValue NVARCHAR(255)
                    );

INSERT INTO @tbl2
VALUES
       (
       'VendorPlantName','Plant 2'
       ),
       (
       'VendorPlantName','Plant 1'
       );

- Cte создает список исключений

WITH ExcludeFields(ExclFld) AS
(
    SELECT NodeName AS [@nn]
          ,NodeValue AS [@nv] 
    FROM @tbl2 t2
    FOR XML PATH('exclFld'),TYPE
)
SELECT Combined.query('
                      <Root> 
                      { 
                          for $rw in /row
                          return 
                          <row>
                          {
                            for $fld in $rw/*
                            return
                            if(/exclFld[@nn=local-name($fld)]/@nv=$fld/text()) then
                            <x/>
                            else
                            $fld
                          }
                          </row>
                      }
                      </Root>
                      ')
               .query('/Root/row[empty(x)]')

FROM ExcludeFields ef
CROSS JOIN @tbl1 t1
CROSS APPLY(SELECT ef.ExclFld AS [*]
                  ,t1.XMLValue.query('/Root/row')
            FOR XML PATH(''),TYPE) A(Combined);

Идея вshort:

Мы создаем промежуточный XML, содержащий ваши строки и список исключаемых полей:

<exclFld nn="VendorPlantName" nv="Plant 2" />
<exclFld nn="VendorPlantName" nv="Plant 1" />
<row>
  <VendorPlantName>Plant 1</VendorPlantName>
  <TotalAttainableUnits>3693</TotalAttainableUnits>
</row>
<row>
  <VendorPlantName>Plant 2</VendorPlantName>
  <TotalAttainableUnits>69477</TotalAttainableUnits>
</row>
<row>
  <VendorPlantName>Plant 3</VendorPlantName>
  <TotalAttainableUnits>573</TotalAttainableUnits>
</row>

Нет, мы не можем использовать подход XQuery-FLWOR, чтобы проходить через каждую строку, а затем через каждуюполе и возвращает <x/>, если комбинация имени узла и значения узла существует в списке исключаемых полей.

С помощью этого XML мы можем запустить другой XQuery, получая только строки, в которых нет <x>.

Другой подход

... Вы можете уничтожить это и использовать стандартный SQL для тяжелой работы:

WITH TheRows AS
(          
    SELECT ROW_NUMBER() OVER(ORDER BY A.rw) AS RowNumber
          ,rw.query('.') TheRow
    FROM @tbl1 t1
    CROSS APPLY t1.XMLValue.nodes('/Root/row') A(rw)
)
,EAV AS
(
    SELECT r.RowNumber
          ,fld.value('local-name(.)','nvarchar(1000)') AS FieldName
          ,fld.value('text()[1]','nvarchar(1000)') AS FieldValue 
    FROM TheRows r
    CROSS APPLY r.TheRow.nodes('row/*') Each(fld)
)
,SkipRows AS
(
    SELECT EAV.RowNumber
    FROM EAV
    WHERE EXISTS(SELECT 1 FROM @tbl2 t2 WHERE t2.NodeName=EAV.FieldName AND t2.NodeValue=EAV.FieldValue)
    GROUP BY EAV.RowNumber
)
SELECT *
FROM EAV
WHERE EAV.RowNumber NOT IN (SELECT sr.RowNumber FROM SkipRows sr);

Последний результатможет быть легко возвращено как XML - при необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...