【问题标题】:SQL merge functionality without specifying column names不指定列名的 SQL 合并功能
【发布时间】:2013-02-28 20:13:36
【问题描述】:

我正在从我的 Web 应用程序执行 SQLBulkCopy 并将记录插入到临时表中。这是我第一次使用临时表。将接受数据的实时表有大约 200 个字段,并且可以更改为未来。当发生这种变化时,我不想重新编写合并语句。

我想出了这个模仿合并功能的 SQL,但不需要我拼出表格列。我不是 SQL 专家,我希望有人来看看,如果您发现使用此 SQL 可能出现的任何问题,请告诉我,因为我没有看到任何此类示例,也没有很多人在搜索。

请注意,暂存表中具有空 id 字段的记录将被插入。

-- set the table names, primary key field & vars to hold query parts
DECLARE @LiveTable varchar(20) = 'Test'
DECLARE @StagingTable varchar(20) = 'TestStaging'
DECLARE @PKField varchar(20) = 'TestPK'
DECLARE @SQLSet nvarchar(MAX) = ''
DECLARE @SQLInsertFields nvarchar(MAX) = ''

-- get comma delimited field names
DECLARE @Fields nvarchar(MAX) = (SELECT dbo.fn_GetCommaDelimitedFieldNames(@LiveTable))

-- loop through fields generating set clause of query to execute
WHILE LEN(@Fields) > 0
BEGIN
    DECLARE @Field varchar(50) = left(@Fields, CHARINDEX(',', @Fields+',')-1)
    IF @Field <> @PKField -- the primary key field cannot be updated
        BEGIN
            SET @SQLSet += ', ' + @LiveTable + '.' + @Field + ' = ' + @StagingTable + '.' + @Field
            SET @SQLInsertFields += ', ' + @Field
        END
    SET @Fields = STUFF(@Fields, 1, CHARINDEX(',', @Fields+','), '')
END

-- remove the leading comma
SET @SQLSet = SUBSTRING(@SQLSet,3,LEN(@SQLSet))
SET @SQLInsertFields = SUBSTRING(@SQLInsertFields,3,LEN(@SQLInsertFields))

-- update records from staging table where primary key is provided
DECLARE @SQL nvarchar(MAX) = N'UPDATE ' + @LiveTable + 
                             ' SET ' + @SQLSet + 
                             ' FROM ' + @LiveTable + 
                             ' INNER JOIN ' + @StagingTable + 
                             ' ON ' + @LiveTable + '.' + @PKField + ' = ' + @StagingTable + '.' + @PKField

-- insert records from staging table where primary key is null
SET @SQL += '; INSERT INTO ' + @LiveTable + ' (' + @SQLInsertFields + ') SELECT ' + @SQLInsertFields + ' FROM ' + @StagingTable + ' WHERE ' + @PKField + ' IS NULL'

-- delete the records from the staging table
SET @SQL += '; DELETE FROM ' + @StagingTable

-- execute the sql statement to update existing records and insert new records
exec sp_executesql @SQL;

如果有人发现性能或其他方面有任何问题,我很感激你的洞察力。

【问题讨论】:

  • 您可能希望将其作为事务执行,这样在插入或更新时不会出现错误,然后删除临时表。
  • @Hogan - 好点,肯定会进行交易。感谢您的意见。
  • 一张表中有 200 个字段?是关系文件还是平面文件?
  • @Dan - 有一些关系数据。该表是一个 MLS 列表表,包含价格、平方英尺、床、浴室等数据。实际上,它大约有 175 个字段构成所有属性类型共享的属性以及一些后台数据,例如代理信息、销售条款、等等......所有属性类型之间不通用的数据已被规范化到它们自己的表中。

标签: sql sql-server merge dynamic-sql


【解决方案1】:

不要这样做。真的。你正在努力避免一个罕见的问题,到时候你可能无法正确处理。

如果目标表发生变化,你怎么知道它会以这样一种方式发生变化,以使你喜欢的动态 SQL 能够正常工作?你怎么能确定它不会似乎工作——即在语法上会工作——但实际上做错了事?如果目标表发生更改,您是否不必更改应用程序以及临时表?有了这一切,又增加了一个SET 子句是什么?

与此同时,怎么能指望任何人阅读那个 gobbledygook(不是你的错,真的,那是 SQL 的语法)?沼泽标准插入语句将非常清晰和可靠。

而且速度很快。 SQL Server 无法优化您的动态查询。您使用 bcp 是为了提高效率,而现在您正在以具有良好意义的面向未来的能力击败它。

【讨论】:

  • 感谢您的意见,我们将予以高度考虑。我将测试这个与 MERGE 的性能,如果有很大的差异,我会走这条路并手动保持 MERGE 语句。我想不出一个不能正确执行的场景,也许你可以提供一个例子?有一个触发器可以使暂存表的结构与目标表保持同步,因此那里不会有问题。如果目标表发生变化,我的应用程序中唯一的变化就是更新 EntityModel,但不会更改代码,因为来自源的映射是动态的。
猜你喜欢
  • 1970-01-01
  • 2020-02-27
  • 1970-01-01
  • 2018-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多