问题描述
我有一个xml,可为我提供表名以及相应的ID列,并根据以下ID来更新或插入该列:
<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
解决方法
您可以将以下内容用作基础:
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_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;