Sql server 如何为不同的源数据库/表版本编写T-SQL脚本
我的主要问题是: 如果源表没有包含它可能具有的所有字段,如何编写可以处理的insert SQL查询。有关原因,请参见“背景”一节 因此,我的目标表有一组已知的字段,但我的源表可能缺少其中一些字段。像这样:Sql server 如何为不同的源数据库/表版本编写T-SQL脚本,sql-server,Sql Server,我的主要问题是: 如果源表没有包含它可能具有的所有字段,如何编写可以处理的insert SQL查询。有关原因,请参见“背景”一节 因此,我的目标表有一组已知的字段,但我的源表可能缺少其中一些字段。像这样: Destination.TableA ---------- ID Field 1 Field 2 Field 3 source1.TableA ----- ID Field 2 source2.TableA ------------- ID Field 1 Field 2 所以我基本上需
Destination.TableA
----------
ID
Field 1
Field 2
Field 3
source1.TableA
-----
ID
Field 2
source2.TableA
-------------
ID
Field 1
Field 2
所以我基本上需要的是一个等价于if的字段。。。存在,然后插入到。。。。主键不应受到影响
我知道如何测试表或其他SQL对象的存在性,但我不知道如何在有关表字段的insert语句中进行测试。-你能给我一个提示吗
一点背景:
我们有很多客户运行我们产品的不同版本。随着时间的推移,我们的数据库已经一个接一个地扩展。一些客户数据库已根据这些客户的个人需求进行了定制
现在,我正在开发一个工具/脚本,将所有这些旧版本升级到当前的数据库版本。由于我们对最新版本进行了重构,因此此次升级需要大量修补/移动数据,因此我想构建一个能够升级源数据库所有版本的脚本。-这主要是为了在开发脚本时避免重复调整
因为我已经有了生成SQL查询的工具,所以生成的查询代码的数量是其功能的次要部分
p、 在这次升级之后,我们已经有了内置的升级机制来简化将来的升级。本脚本旨在更新我们2018年前的所有产品,使其进入我们的新产品/升级周期
p、 另外,所有数据库都运行在Microsoft SQL Server上好的,这将是一个相当混乱的答案,因为这里有很多事情可以让它以合理合理的方式工作。我使用合并和动态SQL生成脚本,然后为每个表运行它们 首先,我需要一些测试数据,所以我创建了一个临时数据库来使用:
USE Temp;
GO
CREATE SCHEMA destination;
GO
CREATE SCHEMA source1;
GO
CREATE SCHEMA source2
GO
CREATE TABLE destination.tableA (
ID INT,
Field1 VARCHAR(50),
Field2 VARCHAR(50),
Field3 VARCHAR(50));
GO
CREATE TABLE source1.tableA (
ID INT,
Field2 VARCHAR(50));
GO
CREATE TABLE source2.tableA (
ID INT,
Field1 VARCHAR(50),
Field2 VARCHAR(50));
GO
然后我添加了一些测试数据,使其可重复:
DELETE FROM destination.tableA;
DELETE FROM source1.tableA;
DELETE FROM source2.tableA;
INSERT INTO source1.tableA SELECT 1, 'dog';
INSERT INTO source1.tableA SELECT 2, 'cat';
INSERT INTO source2.tableA SELECT 1, 'dog', 'harold';
INSERT INTO source2.tableA SELECT 3, 'mouse', 'midge';
GO
计划是从一个完全空的目标表开始,在某些情况下,所有数据都来自一个表,而在其他情况下,数据是混合的,例如,一个表提供一段数据,在另一个表中为同一主键设置另一列。这使得事情变得更加复杂,因为现在我们需要根据数据进行插入或更新。这就是合并的意义所在
我还使用FOR XML PATH获取逗号分隔的列表。下面是令人讨厌的动态SQL:
IF OBJECT_ID('tempdb..#schemas') IS NOT NULL
DROP TABLE #schemas;
GO
SELECT SCHEMA_NAME INTO #schemas FROM INFORMATION_SCHEMA.SCHEMATA WHERE CATALOG_NAME = 'Temp' AND SCHEMA_NAME LIKE 'source%';
WHILE EXISTS (SELECT * FROM #schemas)
BEGIN
DECLARE @schema VARCHAR(50);
SELECT TOP 1 @schema = SCHEMA_NAME FROM #schemas;
SELECT @schema;
DECLARE @sql NVARCHAR(4000);
SELECT @sql = N'MERGE destination.tableA AS [target] USING (SELECT * FROM ' + QUOTENAME(@schema) + '.tableA)
AS [source] ('
+ STUFF((SELECT ',' + COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG = 'Temp' AND TABLE_SCHEMA = @schema AND TABLE_NAME = 'TableA' ORDER BY ORDINAL_POSITION FOR XML PATH('')), 1, 1, '')
+ N') ON [target].ID = [source].ID
WHEN MATCHED THEN UPDATE SET '
+ STUFF((SELECT ',' + COLUMN_NAME + ' = [source].' + COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG = 'Temp' AND TABLE_SCHEMA = @schema AND TABLE_NAME = 'TableA' AND COLUMN_NAME != 'ID' ORDER BY ORDINAL_POSITION FOR XML PATH('')), 1, 1, '')
+ N' WHEN NOT MATCHED THEN INSERT ('
+ STUFF((SELECT ',' + COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG = 'Temp' AND TABLE_SCHEMA = @schema AND TABLE_NAME = 'TableA' ORDER BY ORDINAL_POSITION FOR XML PATH('')), 1, 1, '')
+ N')
VALUES ('
+ STUFF((SELECT ', [source].' + COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG = 'Temp' AND TABLE_SCHEMA = @schema AND TABLE_NAME = 'TableA' ORDER BY ORDINAL_POSITION FOR XML PATH('')), 1, 1, '')
+ ');';
EXEC sp_executesql @sql;
SELECT @sql;
DELETE FROM #schemas WHERE SCHEMA_NAME = @schema;
END;
GO
SELECT * FROM [destination].tableA;
下面是一个运行的动态SQL脚本示例:
MERGE destination.tableA AS [target] USING (SELECT * FROM [source1].tableA)
AS [source] (ID,Field2) ON [target].ID = [source].ID
WHEN MATCHED THEN UPDATE SET Field2 = [source].Field2 WHEN NOT MATCHED
THEN INSERT (ID,Field2)
VALUES ( [source].ID, [source].Field2);
其最终结果是:
ID Field1 Field2 Field3
1 dog harold NULL
2 NULL cat NULL
3 mouse midge NULL
我觉得哪个好
但是,我想您需要做一些更改才能使其与您的环境配合使用。我希望这能让您对如何实现这一点有一些想法?如果您真的想使用SQL脚本来实现这一点,那么您可以使用动态SQL?也许有一个脚本可以对完整的列集进行数据插入,该脚本在另一个脚本提前添加新列后运行,沿着if not exists从sys.columns中选择1,其中object_id=object_id'dbo.TableA'和name='Field2',然后更改table dbo.TableA添加Field2 someDataType someConstraints;对于所需的每一列。@Stefan,您使用的是哪一版本的SQL?@Birel MSSQL 2018Well,那么,我认为AlwaysLearning建议是一个非常好的解决方案-它可能比尝试为每个客户构建不同的insert…select语句更简单。哇,您将所有内容都放在这里了,谢谢您=我并不特别需要合并。我可以从表名判断它是否需要更新或插入。但是,您将dynamicSQL组合在一起的方式比我预期的要优雅得多。*更多-…比。。。