Как экспортировать целые элементы и атрибуты xml и сохранить их на SQL Server в одной таблице

Я хотел экспортировать все элементы, атрибуты XML из https://s3.amazonaws.com/irs-form-990/200931393493000150_public.xml. Как показано в моем коде ниже, я смог создать таблицы, используя несколько запросов. Похоже, мне нужно создать несколько таблиц из нескольких элементов и атрибутов, что будет утомительным процессом для объединения всех или объединения соответственно.

Мой вопрос: есть ли способ создать одну единственную таблицу за один раз всего содержимого XML?

Второй вопрос: как использовать openrowset для чтения из XML-файла, хранящегося в SQL Server, а не в локальном каталоге.

Вот код, который я использовал, который отлично работает для запроса части XML:

Create database [IRS-900]
USE [IRS-900]
GO

CREATE TABLE [SampleXML]
(
Id INT IDENTITY PRIMARY KEY,
XMLData XML,
LoadedDateTime DATETIME
)

INSERT INTO [SampleXMl](XMLData, LoadedDateTime)
SELECT CONVERT(XML, BulkColumn) AS BulkColumn, GETDATE() 

FROM OPENROWSET(BULK 'C:\Users\200931393493000150_public.xml', SINGLE_BLOB) AS x;



     DECLARE @targetTable TABLE 
     
     (
        ID INT IDENTITY PRIMARY KEY,  
        EIN CHAR(10), 
        AddressLine1 VARCHAR(50),
        AddressLine2 VARCHAR(50),
        City VARCHAR(50),
        State CHAR(2),
        ZIPCode CHAR(5)
        
     );

      ;WITH XMLNAMESPACES (DEFAULT 'http://www.irs.gov/efile')
     
    , rs (xmldata) AS
    (
       SELECT TRY_CAST(BulkColumn AS XML) AS BulkColumn 
       FROM OPENROWSET(BULK 'C:\Users\200931393493000150_public.xml', SINGLE_BLOB) AS x
      -- xml file from (https://s3.amazonaws.com/irs-form-990/200931393493000150_public.xml)  saved to local disk 
    )

    INSERT INTO @targetTable
    --Insert Into FormIndex
    --Insert into IRS900Sample
    SELECT 
       -- c.value('(Key/text())[1]', 'Varchar(100)') AS Keys
         c.value('(EIN/text())[1]', 'CHAR(10)') AS EIN
       , c.value('(USAddress/AddressLine1/text())[1]','VARCHAR(50)') AS AddressLine1
       , c.value('(USAddress/AddressLine2/text())[1]','VARCHAR(50)') AS AddressLine2
       , c.value('(USAddress/City/text())[1]','VARCHAR(100)') AS City
       , c.value('(USAddress/State/text())[1]','CHAR(2)') AS State
       , c.value('(USAddress/ZIPCode/text())[1]','CHAR(5)') AS ZIPCode
    FROM rs AS tbl
       --CROSS APPLY tbl.xmldata.nodes('/Return/ReturnHeader/Filer')  AS t(c);
         OUTER APPLY tbl.xmldata.nodes('/Return/ReturnHeader/Filer')  AS t(c);
       --querying the index 
       --CROSS APPLY tbl.xmldata.nodes ('/ListBucketResult/Contents') AS t(c);
       --OUTER APPLY tbl.xmldata.nodes ('/ListBucketResult/Contents') AS t(c);

    SELECT * FROM @targetTable;
    --select * from FormIndex
    --SELECT * FROM IRS900Sample

 DECLARE @targetTable2 TABLE 
      --create table IRS900Sample
     (
        ID INT IDENTITY PRIMARY KEY,  
        NamePerson VARCHAR(50),
        Title VARCHAR(50),
        AverageHoursPerWeek FLOAT,
        IndividualTrusteeOrDirector CHAR(2),
        ReportableCompFromOrganization  CHAR(2),
        ReportableCompFromRelatedOrgs CHAR(2),
        OtherCompensation CHAR(2)
     );

    
      ;WITH XMLNAMESPACES (DEFAULT 'http://www.irs.gov/efile')
    
    , rs (xmldata) AS
    (
       SELECT TRY_CAST(BulkColumn AS XML) AS BulkColumn 
      
      FROM OPENROWSET(BULK 'C:\Users\200931393493000150_public.xml', SINGLE_BLOB) AS x
      -- xml file from (https://s3.amazonaws.com/irs-form-990/200931393493000150_public.xml)  saved to local disk 
       
    )

    INSERT INTO @targetTable
    --Insert Into FormIndex
    --Insert into IRS900Sample
    SELECT 
       
         c.value('(NamePerson/text())[1]','VARCHAR(50)') AS NamePerson
       , c.value('(Title/text())[1]','VARCHAR(50)') AS Title
       , c.value('(AverageHoursPerWeek/text())[1]','float') AS AverageHoursPerWeek
       , c.value('(IndividualTrusteeOrDirector/text())[1]','CHAR(2)') AS IndividualTrusteeOrDirector
       , c.value('(ReportableCompFromOrganization/text())[1]','CHAR(2)') AS ReportableCompFromOrganization
       , c.value('(ReportableCompFromRelatedOrgs/text())[1]','CHAR(2)') AS ReportableCompFromRelatedOrgs
       , c.value('(OtherCompensation/text())[1]','CHAR(2)') AS OtherCompensation
    FROM rs AS tbl
      
          OUTER APPLY tbl.xmldata.nodes('/Return/ReturnData/IRS990/Form990PartVIISectionA')  AS t(c);
        --CROSS APPLY tbl.xmldata.nodes('/Return/ReturnData/IRS990/Form990PartVIISectionA')  AS t(c);

    SELECT * FROM @targetTable2;
    

person Divola    schedule 11.08.2020    source источник


Ответы (2)


То, что вы сейчас делаете, называется измельчением, то есть измельчением XML, преобразованием его в прямоугольный/реляционный формат и, в конечном итоге, загрузкой в ​​таблицы.

Вопрос №1. Есть ли способ создать одну единственную таблицу за один раз для всего содержимого xml?

Ответ: можно загрузить весь файл XML как есть в один столбец типа данных XML.

Вопрос 2. Как использовать openrowset для чтения из XML-файла, хранящегося в SQL Server, а не в локальном каталоге?

Ответ: методы SQL Server XQuery .nodes() и .value() как раз для этого.

person Yitzhak Khabinsky    schedule 11.08.2020
comment
для вопроса номер один, как вы можете видеть в коде, я создал таблицу xml. то есть загрузил весь файл XML как есть в один столбец типа данных XML. Мой вопрос заключается в том, как импортировать все элементы и атрибуты в одну таблицу. Таким образом, я могу запрашивать как обычную таблицу, а не как тип данных xml. Второй вопрос: вместо этого я использую openrowset (для чтения с локального пути), как я могу прочитать файл из файла, уже загруженного на сервер sql, в виде файла xml. Я хочу использовать подзапрос для openrowset, например select * from [IRS-900] (это тип данных xml, который я создал на сервере sql) - person Divola; 12.08.2020
comment
@Divola, пожалуйста, свяжитесь со мной в LinkedIn. - person Yitzhak Khabinsky; 12.08.2020
comment
Конечно Спасибо за предложение - person Divola; 18.08.2020

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

ИСПОЛЬЗОВАТЬ:

select *  from Utility.FlattenXml('<a>1</a><b></b><c>3</c>')
select field, field  from Utility.FlattenXml('<a>1</a><b></b><c>3</c>') where [yourfield] = [value]

ПРОЦЕДУРА:

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
person Ray    schedule 17.08.2020
comment
Спасибо за помощь, откуда берется схема утилиты? Другой вопрос: когда я поместил свой xml-файл, хранящийся в таблице sql, функция вернула пустую таблицу. Вот вызов функции, который я использовал, select * from FlattenXml('SELECT [xmldata] FROM [IRS-900].[dbo].[XMLTable], где ID=1') У меня есть тысячи файлов данных xml, хранящихся в таблице, которую я хотел повторите измельчение XML из. Я просто тестировал код в одном xml-данном. Спасибо еще раз - person Divola; 18.08.2020
comment
Мне жаль, что схема взята из моей БД, вы можете создать свою собственную! Это действительно не имеет значения. Проблема в том, что вы отправляете выбор в качестве параметра, а функция ожидает и XML. Попробуйте это: DECLARE @x XML SELECT @x = [xmldata] FROM [IRS-900].[dbo].[XMLTable], где ID=1 выберите * из [YourSchema].FlattenXml(@x) Также помните, что функция возвращает таблица с различными форматами XML, вам нужно решить, какие поля вы хотите, изменив select * с [yourschema].flattenxml на значение select, ... from [yourschema].flattenxml - person Ray; 19.08.2020