Динамическое создание таблиц из XML-документа. Имя каждого родительского узла в виде новой таблицы и имя дочернего узла в виде столбцов - PullRequest
0 голосов
/ 06 ноября 2019

У меня есть требование, при котором я должен динамически создавать таблицы из XML-документа. Каждый родительский узел должен иметь таблицу create с именем родительского узла / тега, а его дочерние теги будут столбцом таблицы.

Например:

Добавление / исправление XML, поскольку исходный опубликованный XML имеет ошибки синтаксического анализа

<ROOT>
  <Store>
    <TAN />
    <CREATED_BY />
    <DATE>1990-01-01</DATE>
    <TYPE />
    <Quotes />
    <ORDER>ORD#67854</ORDER>
    <ORDER_DET>
      <ORDER_ADD />
      <ORDER_MODE />
      <ORDER_ID />
    </ORDER_DET>
    <PAY_DET>
      <PAY_ADD />
      <PAY_MOD />
    </PAY_DET>
  </Store>
  <RETURN>
    <RETURN_ORDER_NO />
    <RETURN_PRODUCTS>
      <PRODUCT>
        <RET_NUMBER>76398793</RET_NUMBER>
        <RET_ITEM>0001</RET_ITEM>
        <RET_RES>AHV7897</RET_RES>
        <RET_INF>75587867</RET_INF>
        <Quotes>678</Quotes>
        <TAN />
        <RET_IT>0078</RET_IT>
        <STORE_LOC>56</STORE_LOC>
        <VAL>890</VAL>
        <DESC>STOVE 4X</DESC>
        <CMS>3468</CMS>
        <BTD>987</BTD>
        <QNTY>8</QNTY>
        <MRP>870.0000</MRP>
        <PRICE_QNTY>900</PRICE_QNTY>
        <KYC>1</KYC>
        <DELAY>1</DELAY>
        <SELLER>ROSH</SELLER>
        <PCKG_NO>32156</PCKG_NO>
        <PLAN_DEL>30</PLAN_DEL>
        <PO_QNTY_ISO>ROSHR</PO_QNTY_ISO>
        <SELLER_ISO>ROSHR</SELLER_ISO>
        <SELLER_NAME>ABC</SELLER_NAME>
        <SELLER_COST>20000</SELLER_COST>
        <SELLER_TYPE>2</SELLER_TYPE>
      </PRODUCT>
      <PRODUCT>
        <RET_NUMBER>9585623235</RET_NUMBER>
        <RET_ITEM>00001</RET_ITEM>
        <RET_RES>PUH5634</RET_RES>
        <RET_INF>656266</RET_INF>
        <Quotes>110</Quotes>
        <TAN />
        <RET_IT>98562</RET_IT>
        <STORE_LOC>1000</STORE_LOC>
        <VAL>8569</VAL>
        <DESC>STOVE 4X</DESC>
        <CMS>9582</CMS>
        <BTD>1202</BTD>
        <QNTY>ROSH</QNTY>
        <MRP>858</MRP>
        <PRICE_QNTY>52</PRICE_QNTY>
        <KYC>1</KYC>
        <DELAY>1</DELAY>
        <SELLER>ROSH</SELLER>
        <PCKG_NO>154026</PCKG_NO>
        <PLAN_DEL>98</PLAN_DEL>
        <PO_QNTY_ISO>ROSHR</PO_QNTY_ISO>
        <SELLER_ISO>ROSHR</SELLER_ISO>
        <SELLER_NAME>ABC</SELLER_NAME>
        <SELLER_COST>857458</SELLER_COST>
        <SELLER_TYPE>2</SELLER_TYPE>
      </PRODUCT>
      <PRODUCT>
        <RET_NUMBER>852574585</RET_NUMBER>
        <RET_ITEM>8548</RET_ITEM>
        <RET_RES>APY8264</RET_RES>
        <RET_INF>8542365</RET_INF>
        <Quotes>0</Quotes>
        <TAN />
        <RET_IT>85232</RET_IT>
        <STORE_LOC>5856</STORE_LOC>
        <VAL>8565</VAL>
        <DESC>STOVE 4X</DESC>
        <CMS>2323</CMS>
        <BTD>3030</BTD>
        <QNTY>ROSH</QNTY>
        <MRP>235</MRP>
        <PRICE_QNTY>8256</PRICE_QNTY>
        <KYC>1</KYC>
        <DELAY>1</DELAY>
        <SELLER>ROSH</SELLER>
        <PCKG_NO>2659585</PCKG_NO>
        <PLAN_DEL>856</PLAN_DEL>
        <PO_QNTY_ISO>ROSHR</PO_QNTY_ISO>
        <SELLER_ISO>ROSHR</SELLER_ISO>
        <SELLER_NAME>ABC</SELLER_NAME>
        <SELLER_COST>8565</SELLER_COST>
        <SELLER_TYPE>2</SELLER_TYPE>
      </PRODUCT>
      <PRODUCT>
        <RET_NUMBER>021546232</RET_NUMBER>
        <RET_ITEM>32323</RET_ITEM>
        <RET_RES>THY67876</RET_RES>
        <RET_INF>6500856</RET_INF>
        <Quotes>0</Quotes>
        <TAN />
        <RET_IT>03236</RET_IT>
        <STORE_LOC>5235</STORE_LOC>
        <VAL>5232</VAL>
        <DESC>STOVE 4X</DESC>
        <CMS>35362</CMS>
        <BTD>5656</BTD>
        <QNTY>ROSH</QNTY>
        <MRP>525</MRP>
        <PRICE_QNTY>521</PRICE_QNTY>
        <KYC>1</KYC>
        <DELAY>1</DELAY>
        <SELLER>ROSH</SELLER>
        <PCKG_NO>123513213</PCKG_NO>
        <PLAN_DEL>1212</PLAN_DEL>
        <PO_QNTY_ISO>ROSHR</PO_QNTY_ISO>
        <SELLER_ISO>ROSHR</SELLER_ISO>
        <SELLER_NAME>ABC</SELLER_NAME>
        <SELLER_COST>124154</SELLER_COST>
        <SELLER_TYPE>2</SELLER_TYPE>
      </PRODUCT>
    </RETURN_PRODUCTS>
  </RETURN>
</ROOT>

Должны быть созданы следующие таблицы и ссылки на внешние ключи (узел родитель-потомок)отношение), когда этот XML предоставляется в качестве параметра.

Table1: [tblXMLOrder_Det]

ORDER_ADD   ORDER_MODE  ORDER_ID    Store_Id
NULL        NULL        NULL         1

Table2: [tblXMLProduct]

enter image description here

Table3: [tblXMLPay_DET]

enter image description here

Table 4: [tblXMLStore]

enter image description here

Table5: [tblXMLReturn]

enter image description here

Таблица 6: [tblXMLRetun_Products]

enter image description here

Я реализовал эти таблицы с помощью функции импорта SSIS XML (с режимом доступа к данным «Таблица или Просмотр - быстрая загрузка»), в котором мы должны вручную создать XSD, имя таблицы и разделитьOLE DB Направления.

Есть ли способ, с помощью которого мы можем сделать это динамически в SSIS. Например, в случае передачи нового XML-документа в качестве параметра, эти таблицы и отношение родительский / дочерний узел / таблица могут быть созданы автоматически.

TSQL:

Я обнаружил следующий UDF SQL вstackoverflow, который создал таблицу метаданных XML-документа

CREATE FUNCTION [dbo].[XMLCreateTable](@x 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 @x.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
GO

Я создал следующий скрипт для динамического создания таблиц из метаданных XML (как o / p UDF), однако, обнаружив трудности при создании одной таблицы, когдавнутренние теги повторяются. Например, несколько тегов под родительским тегом.

declare @XML xml = '<XML Document>'
select * into #tblXMLCreateTable from [dbo].[XMLCreateTable](@xml)

SET NOCOUNT ON 

DECLARE @depth INT, @ParentName VARCHAR(4000), @NodeName VARCHAR(4000), @FullPath VARCHAR(4000), @XPath VARCHAR(4000), @Value VARCHAR(4000)
DECLARE @CreateTableSQL VARCHAR(MAX)
DECLARE @colName VARCHAR(MAX)


DECLARE depth_cursor CURSOR FOR 
    SELECT DISTINCT depth d FROM #tblXMLCreateTable order by d 

OPEN depth_cursor
FETCH NEXT  FROM depth_cursor INTO @depth
WHILE @@FETCH_STATUS = 0
BEGIN

    DECLARE records_by_depth_cursor CURSOR FOR 
        SELECT ParentName, NodeName, FullPath, XPath, [Value] FROM #tblXMLCreateTable WHERE Depth = @depth order by id
    OPEN records_by_depth_cursor
    FETCH NEXT FROM records_by_depth_cursor INTO 
        @ParentName, @NodeName, @FullPath, @XPath, @Value
            WHILE @@FETCH_STATUS = 0
                BEGIN

                    --SELECT @depth, @ParentName, @NodeName, @FullPath, @XPath, @Value


                    SELECT @colName = LEFT(NodeName, LEN(NodeName) - 1)
                            FROM (
                                SELECT NodeName + ' VARCHAR(4000),'
                                FROM #tblXMLCreateTable WHERE Depth = @depth + 1 AND (ParentName = @NodeName OR ParentName IS NULL)
                                ORDER BY NodeName
                                FOR XML PATH ('')
                              ) c (NodeName)

                    SET @CreateTableSQL = 'CREATE TABLE '+@NodeName+'(ID BIGINT DEFAULT format(getdate(),''yyyyMMddHHmmssffff''), '+ @colName +')' 

                    PRINT @CreateTableSQL

                    FETCH NEXT FROM records_by_depth_cursor INTO 
                        @ParentName, @NodeName, @FullPath, @XPath, @Value               
                END

                    CLOSE records_by_depth_cursor
                    DEALLOCATE records_by_depth_cursor

    FETCH NEXT  FROM depth_cursor INTO @depth
END
CLOSE depth_cursor
DEALLOCATE depth_cursor

Этот код SQL имеет ограничение, например, повторяющиеся имена таблиц, например, сценарий создания таблицы PRODUCT, создается несколько раз. Кроме того, я испытываю трудности с вставкой данных и ведением родительско-дочерних ссылок.

Пожалуйста, дайте мне знать, если мы сможем улучшить этот код для динамической обработки требования с различными документами XML. т.е. с каждым XML необходимо создать новую родительскую таблицу, и это приведет к ошибке в случае, если тот же тег / таблица уже был создан в предыдущей обработке XML.

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