【问题标题】:MERGE Command in SQL ServerSQL Server 中的 MERGE 命令
【发布时间】:2011-04-04 02:07:02
【问题描述】:

我一直在使用语句

insert into target 
select * from source 
where [set of conditions] for a while.

最近发现这个MERGE命令会更有效地用于我的目的,以便我可以将上述语句更改为

MERGE target 
USING source ON [my condtion] 
WHEN NOT MATCHED BY TARGET 
THEN INSERT VALUES (source.col1, source.col2, source.col3) 

但是对我来说,问题是如果我的源表中有 20 多列,我必须列出所有这些列,我需要一种方法来指定它来插入 source.* 。有办法吗?我是 SQL 新手。感谢你的帮助。

提前致谢:)

【问题讨论】:

  • @campbell - 谢谢你的信息..

标签: sql sql-server-2008 merge insert


【解决方案1】:

我也是;我讨厌输入列名。

我通常在动态 SQL 中构建 Merge 语句。
我有一个函数,它以表名作为参数,并返回一个字符串,其中包含所有列名,格式正确,带有表名前缀、[] 括号和逗号,如 S.Col1、S.Col2、S.Col3

我还可以告诉您,我构建了一个包含所需列的临时表,并将临时表传递给我的函数,因为有时您不想要所有列的列表。但这可能会令人困惑,模糊了重要的部分;

  1. 使用动态sql
  2. 使用函数创建列的 csv 列表。

【讨论】:

  • hmm.. 这会让事情变得更容易.. 会试试这个。
【解决方案2】:

我读到的关于MERGE 语句的所有内容都表明您需要为INSERT 语句指定列。如果您正在寻找一种快速获取INSERT 语句的方法,您可以在SSMS 中的表格上单击鼠标右键,然后选择Script Table As->INSERT To->Clipboard。然后,您可以将其粘贴到查询中并仅更改 VALUES 部分。

Merge statement

【讨论】:

    【解决方案3】:

    在这种情况下使用 MERGE 根本没有任何优势。为什么过于复杂?坚持 KISS 原则,这是天赐良机。

    无论如何,这是脚本:

    declare 
        @targetTableName varchar(100) = 'target'
        ,@targetSchemaName varchar(20) = 'dbo'
        ,@sourceTableName varchar(100) = 'source'
        ,@sourceSchemaName varchar(20) = 'dbo2'
        ,@matchCondition varchar(50) = 't.id = s.id'
        ,@columns varchar(max)
    
    set @columns = (select ','+quotename(c.name)
    from sys.tables t 
    join sys.columns as c on t.object_id = c.object_id
    join sys.schemas s on s.schema_id = t.schema_id
    where t.name = @targetTableName and s.name = isnull(@targetSchemaName, s.name)
    for xml path(''))
    
    --a column name starts with a comma
    
    declare @sql varchar(max) =  '
    merge @target t 
    using @source s on @matchCondition
    when not matched then 
    insert (@columns)
    values @sourceColumns'
    
    set @sql = 
    replace(replace(replace(replace(replace(@sql
    , '@matchCondition', @matchCondition)
     --replace @columns with column list with the first comma removed
    , '@columns', stuff(@columns, 1, 1, ''))
    --replace @sourceColumns with column list with the 's.' prefix and comma removed
    , '@sourceColumns', stuff(replace(@columns, ',', ',s.'),1,1,''))
    , '@target', quotename(@targetSchemaName)+'.'+quotename(@targetTableName))
    , '@source', quotename(@sourceSchemaName)+'.'+quotename(@sourceTableName))
    
    print @sql
    --exec(@sql)
    

    我们会得到这样的东西:

    merge [dbo].[target] t 
    using [dbo2].[source] s on t.id = s.id
    when not matched then 
    insert ([column1], [column2], [column3], [column4])
    values s.[column1], s.[column2], s.[column3], s.[column4]
    

    【讨论】:

    • Denis- 不,我发现它的优化方式要好得多。我的源表中有超过 50 万行,这导致第一个查询运行了大约 45-50 分钟。对 MERGE 命令的更改使其在几秒钟内完成 :-D :-D 所以我肯定需要更改。
    • 这没有任何意义。 MERGE 简化为简单的 INSERT 不能比后者执行得更好或更差。你刚才说的话,我不能只是绕开我的头。 :D
    • 对于长时间运行的查询,您是否尝试在源表上指定“with(nolock)”?相信我,MERGE 的表现再好不过了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多