Пропустить чтение элемента XML в SQL серверной хранимой процедуре - PullRequest
0 голосов
/ 20 января 2020

У меня есть хранимая процедура в SQL Server 2016, которая читает файл XML. Я хотел бы пропустить сохранение элемента, если его значение отсутствует.

Элемент XML должен выглядеть примерно так

<PROJECT_TERMSX>
      <TERM>Extramural Activities</TERM>
      <TERM>programs</TERM>
      <TERM>research facility</TERM>
</PROJECT_TERMSX>

, но многие элементы сформирован как

<PROJECT_TERMS xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />

Мой код для вставки данных в таблицу:

INSERT into ADMIN_ExPORTER_TERMS (APPLICATION_ID, TERM)
    SELECT
        APPLICATION_ID,
        nref.value('.','varchar(max)') TERM                     
    FROM 
        [ADMIN_Exporter_Files_XML] 
    CROSS APPLY
        XMLData.nodes('//PROJECT_TERMSX/TERM') AS R(nref)
    WHERE
        APPLICATION_ID = @APPLICATION_ID

Я хотел бы пропустить элементы, имеющие схему http, но не знаю, как структурировать код. У меня нет контроля над исходной структурой XML.

Ответы [ 2 ]

0 голосов
/ 22 января 2020

Иногда стандартизация может вам сильно помочь, даже если нужно больше шагов. В моем случае я использовал следующую табличную функцию (см. Ниже) для преобразования файла ANY XML в таблицу SQL Table, и оттуда я могу делать все, что захочу. Хорошо, что мне не нужно кодировать каждое изменение xml, оно динамически преобразуется, и я могу управлять данными как обычной таблицей.

ИСПОЛЬЗОВАНИЕ:

select *  from Utility.FlattenXml('<a>1</a><b></b><c>3</c>')

ПРОЦЕДУРА:

USE [YOUR_DB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [Utility].[FlattenXml](@xmlDoc XML)  
RETURNS TABLE 
AS RETURN 
WITH CTE AS ( 

    SELECT 
            1 AS lvl,  
            x.value('local-name(.)','NVARCHAR(MAX)') AS Name,  
            CAST(NULL AS NVARCHAR(MAX)) AS ParentName, 
            CAST(1 AS INT) AS ParentPosition, 
            CAST(N'Element' AS NVARCHAR(20)) AS NodeType,  
            x.value('local-name(.)','NVARCHAR(MAX)') AS FullPath,  
            x.value('local-name(.)','NVARCHAR(MAX)')  
            + N'[' 
            + CAST(ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS NVARCHAR)  
            + N']' AS XPath,  
            ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS Position, 
            x.value('local-name(.)','NVARCHAR(MAX)') AS Tree,  
            x.value('text()[1]','NVARCHAR(MAX)') AS Value,  
            x.query('.') AS this,         
            x.query('*') AS t,  
            CAST(CAST(1 AS VARBINARY(4)) AS VARBINARY(MAX)) AS Sort,  
            CAST(1 AS INT) AS ID  
    FROM @xmlDoc.nodes('/*') a(x)  
    UNION ALL 
    SELECT 
            p.lvl + 1 AS lvl,  
            c.value('local-name(.)','NVARCHAR(MAX)') AS Name,  
            CAST(p.Name AS NVARCHAR(MAX)) AS ParentName, 
            CAST(p.Position AS INT) AS ParentPosition, 
            CAST(N'Element' AS NVARCHAR(20)) AS NodeType,  
            CAST(p.FullPath + N'/' + c.value('local-name(.)','NVARCHAR(MAX)') AS NVARCHAR(MAX)) AS FullPath,  
            CAST(p.XPath + N'/'+ c.value('local-name(.)','NVARCHAR(MAX)')+ N'['+ CAST(ROW_NUMBER() OVER(PARTITION BY c.value('local-name(.)','NVARCHAR(MAX)') 
            ORDER BY (SELECT 1)) AS NVARCHAR)+ N']' AS NVARCHAR(MAX)) AS XPath,  
            ROW_NUMBER() OVER(PARTITION BY c.value('local-name(.)','NVARCHAR(MAX)')
            ORDER BY (SELECT 1)) AS Position, 
            CAST( SPACE(2 * p.lvl - 1) + N'|' + REPLICATE(N'-', 1) + c.value('local-name(.)','NVARCHAR(MAX)') AS NVARCHAR(MAX)) AS Tree,  
            CAST( c.value('text()[1]','NVARCHAR(MAX)') AS NVARCHAR(MAX) ) AS Value, c.query('.') AS this,  
            c.query('*') AS t,  
            CAST(p.Sort + CAST( (lvl + 1) * 1024 + (ROW_NUMBER() OVER(ORDER BY (SELECT 1)) * 2) AS VARBINARY(4)) AS VARBINARY(MAX) ) AS Sort,  
            CAST((lvl + 1) * 1024 + (ROW_NUMBER() OVER(ORDER BY (SELECT 1)) * 2) AS INT)

    FROM CTE p  
    CROSS APPLY p.t.nodes('*') b(c)), cte2 AS (  
                                                SELECT 
                                                lvl AS Depth,  
                                                Name AS NodeName,  
                                                ParentName, 
                                                ParentPosition, 
                                                NodeType,  
                                                FullPath,  
                                                XPath,  
                                                Position, 
                                                Tree AS TreeView,  
                                                Value,  
                                                this AS XMLData,  
                                                Sort, 
                                                ID
                                                FROM cte  
    UNION ALL 
    SELECT 
            p.lvl,  
            x.value('local-name(.)','NVARCHAR(MAX)'),  
            p.Name, 
            p.Position, 
            CAST(N'Attribute' AS NVARCHAR(20)),  
            p.FullPath + N'/@' + x.value('local-name(.)','NVARCHAR(MAX)'),  
            p.XPath + N'/@' + x.value('local-name(.)','NVARCHAR(MAX)'),  
            1, 
            SPACE(2 * p.lvl - 1) + N'|' + REPLICATE('-', 1)  
            + N'@' + x.value('local-name(.)','NVARCHAR(MAX)'),  
            x.value('.','NVARCHAR(MAX)'),  
            NULL,  
            p.Sort,  
            p.ID + 1  
    FROM CTE p  
    CROSS APPLY this.nodes('/*/@*') a(x)  
    )  

    SELECT 
            ROW_NUMBER() OVER(ORDER BY Sort, ID) AS ID,  
            ParentName, ParentPosition,Depth, NodeName, Position,   
            NodeType, FullPath, XPath, TreeView, Value, XMLData 
    FROM CTE2

Надеюсь, эта помощь!

0 голосов
/ 20 января 2020

Вы почти у цели. XPath выражение .nodes('/PROJECT_TERMSX/TERM') будет отфильтровывать ненужные XML со значениями NULL.

SQL

-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, APPLICATION_ID INT, xmldata XML);
INSERT INTO @tbl (APPLICATION_ID, xmldata)
VALUES (10, N'<PROJECT_TERMSX>
      <TERM>Extramural Activities</TERM>
      <TERM>programs</TERM>
      <TERM>research facility</TERM>
</PROJECT_TERMSX>')
, (10, N'<PROJECT_TERMSX xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />');
-- DDL and sample data population, end

DECLARE @APPLICATION_ID INT = 10;

--INSERT into ADMIN_ExPORTER_TERMS (APPLICATION_ID, TERM)
SELECT APPLICATION_ID
    , col.value('(./text())[1]','VARCHAR(MAX)') AS TERM                     
FROM @tbl AS tbl
    CROSS APPLY tbl.XMLData.nodes('/PROJECT_TERMSX/TERM') AS tab(col)
WHERE APPLICATION_ID = @APPLICATION_ID;

Вывод

+----------------+-----------------------+
| APPLICATION_ID |         TERM          |
+----------------+-----------------------+
|             10 | Extramural Activities |
|             10 | programs              |
|             10 | research facility     |
+----------------+-----------------------+
...