【问题标题】:How to do Merge operation from xml to dynamic tables and columns in MSSQL如何在 MSSQL 中进行从 xml 到动态表和列的合并操作
【发布时间】:2020-10-31 10:09:11
【问题描述】:

我有一个 xml,它为我提供了表名,其中包含相应的 ID 列和要根据下面的这些 ID 更新或插入的列是 xml:

<tables>
    <table>
      <name>Table1</name>
       <attr>
         <id>1</id>
         <columns>
           <col_name>col1</col_name>
           <col_val>123</col_val>
         <columns>
          <columns>
           <col_name>col2</col_name>
           <col_val>345</col_val>
         <columns>
       </attr>
        <attr>
         <id>2</id>
         <columns>
           <col_name>col3</col_name>
           <col_val>123</col_val>
         <columns>
       </attr>
        <attr>
         <id>4</id>
         <columns>
           <col_name>col2</col_name>
           <col_val>123</col_val>
         <columns>
       </attr>
    </table>
        <table>
      <name>Table2</name>
       <attr>
         <id>1</id>
         <columns>
           <col_name>coltb1</col_name>
           <col_val>123</col_val>
         <columns>
          <columns>
           <col_name>coltb3</col_name>
           <col_val>345</col_val>
         <columns>
       </attr>
        <attr>
         <id>3</id>
         <columns>
           <col_name>coltb4</col_name>
           <col_val>123</col_val>
         <columns>
       </attr>
    </table>
    
</tables>

在此我有一个表名,我可以与在我的数据库中创建的表匹配,并且基于 ID 列,我必须检查 ID 是否存在,如果存在,那么我必须更新列存在于带有值的列节点中,如果不存在,我需要插入到表中。

下面是我到目前为止所做的代码,

;WITH CTE (ID,TableName) As (SELECT ROW_NUMBER() OVER (ORDER BY CAST(y.item.query('data(name)') AS NVARCHAR(300))) AS ROWNUM,
                             CAST(y.item.query('data(name)') AS NVARCHAR(300)) AS TableName
                             FROM @input.nodes('/tables/table') y(item))
                             
SELECT  ID,TableName into #tmp FROM CTE

DECLARE @Counter INT,@tableName nvarchar(300)
SET @Counter=1
WHILE (@Counter<=(SELECT Count(*) FROM #tmp))
BEGIN

    SET @tableName=(SELECT TableName FROM #tmp WHERE ID=@Counter)
    ;WITH CTE2(id) AS (SELECT  CAST(x.item.query('data(id)') AS NVARCHAR(30)) AS id
                             FROM @input.nodes('/tables/table') y(item)
                             CROSS APPLY y.item.nodes('./attr') x(item)
                             WHERE CAST(y.item.query('data(name)') AS NVARCHAR(300))=@tableName)
    SELECT * FROM CTE2
    SET @Counter=@Counter+1
END

在这里,我能够获取表名,并且在循环它时,我也得到了 ID,但是,我无法找到列名,也没有得到如何合并这些列名,因为它都是动态的.

我的数据库中的表是

表1:

ID|col1|col2|col3|col4|col5
---------------------------
1 |123 |345 |456 |null|89
2 |222 |444 |667 |890 |99

表2

ID|coltb1|coltb2|coltb3|coltb4|coltb5
------------------------------------
1 |786   |678   |880   |99    |788
2 |345   |678   |667   |9990  |008
3 |344   |667   |623   |945   |678

【问题讨论】:

  • 您将不得不使用动态 SQL,但是,如果这是要求,听起来您更有可能存在设计缺陷。
  • 能否请您建议我还能做什么,XML 准备工作由我完成,我只将其 json 转换为 xml
  • 如果您正在接收 JSON,为什么不将其作为 JSON 使用呢?当 SQL Server 可以读取 JSON 时,将其转换为 XML 似乎是一项毫无意义的任务。
  • 实际上 json 中有垃圾值,我需要在节点中操作并将其转换为 xml,除此之外我不太了解 json 解析 sql
  • 那么我怀疑你没有告诉我们完整的故事,还有设计缺陷。

标签: sql sql-server tsql sql-server-2017 dynamicquery


【解决方案1】:

您可以使用以下内容作为基础:

DECLARE @XML XML = N'<tables><table><name>Table1</name><attr><id>1</id><columns><col_name>col1</col_name><col_val>123</col_val></columns><columns><col_name>col2</col_name><col_val>345</col_val></columns></attr><attr><id>2</id><columns><col_name>col3</col_name><col_val>123</col_val></columns></attr><attr><id>4</id><columns><col_name>col2</col_name><col_val>123</col_val></columns></attr></table><table><name>Table2</name><attr><id>1</id><columns><col_name>coltb1</col_name><col_val>123</col_val></columns><columns><col_name>coltb3</col_name><col_val>345</col_val></columns></attr><attr><id>3</id><columns><col_name>coltb4</col_name><col_val>123</col_val></columns></attr></table></tables>';

DROP TABLE IF EXISTS #DataSource;

CREATE TABLE #DataSource
(
    [table_name] SYSNAME
   ,[id] INT
   ,[column_name] SYSNAME
   ,[column_value] INT
);

WITH DataSource ([table_name], [columns_xml]) AS
(
    SELECT T.c.value('./name[1]', 'NVARCHAR(128)')
          ,T.c.query('./attr') 
    FROM @XML.nodes('/tables/table') T(c)
)
INSERT INTO #DataSource ([table_name], [id], [column_name], [column_value])
SELECT [table_name]
      ,T.c.value('(./id)[1]', 'INT')
      ,T.c.value('(./columns/col_name)[1]', 'VARCHAR(12)')
      ,T.c.value('(./columns/col_val)[1]', 'INT')
FROM DataSource
CROSS APPLY [columns_xml].nodes('./attr') T(c);

SELECT *
FROM #DataSource

DECLARE @current_table_name SYSNAME
       ,@current_columns VARCHAR(MAX)
       ,@current_columns_updated VARCHAR(MAX)
       ,@DynamicTSQLStatement NVARCHAR(MAX);

WHILE EXISTS(SELECT 1 FROM #DataSource)
BEGIN;

    SELECT TOP 1 @current_table_name = [table_name]
    FROM #DataSource;

    SELECT @current_columns = STRING_AGG(QUOTENAME([column_name]), ',')   WITHIN GROUP (ORDER BY [column_name] ASC)
          ,@current_columns_updated  = STRING_AGG(QUOTENAME([column_name]) + ' = ISNULL(S.' + QUOTENAME([column_name]) + ', T.' + QUOTENAME([column_name]) + ')', ',') WITHIN GROUP (ORDER BY [column_name] ASC) 
    FROM #DataSource
    WHERE [table_name] = @current_table_name;

    SET @DynamicTSQLStatement = N'
    WITH DataSource AS
    (
        SELECT *
        FROM
        (
            SELECT [id]
                  ,[column_name]
                  ,[column_value]
            FROM #DataSource
            WHERE [table_name] = ''' + @current_table_name + '''
        ) DS
        PIVOT
        (
            MAX([column_value]) FOR [column_name] IN (' + @current_columns + ')
        ) PVT
    )
    MERGE ' + @current_table_name + ' AS T
    USING DataSource AS S
        ON T.[id] = S.[id]
    WHEN MATCHED THEN  
        UPDATE SET  ' + @current_columns_updated + '
    WHEN NOT MATCHED THEN 
        INSERT ([id], ' + @current_columns + ')
        VALUES (S.[id], ' + @current_columns + ');';
    
    EXEC sp_executesql @DynamicTSQLStatement;

    DELETE FROM #DataSource
    WHERE [table_name] = @current_table_name;

END;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-02
    • 1970-01-01
    • 2020-04-21
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 2023-03-19
    • 1970-01-01
    相关资源
    最近更新 更多