【问题标题】:Insert record only if record does not already exist in table仅当表中不存在记录时才插入记录
【发布时间】:2010-07-16 21:10:02
【问题描述】:

我想知道是否有一种方法可以仅在表中不包含该记录的情况下将记录插入到表中?

是否有可以执行此操作的查询,或者我需要存储过程?

【问题讨论】:

  • 您是否考虑过添加唯一性约束?

标签: sql sql-server tsql


【解决方案1】:

你没有说什么版本的 SQL Server。如果 SQL Server 2008 可以使用MERGE

注意:通常将 Merge 用于 Upsert,这是我最初认为的问题,但它在没有 WHEN MATCHED 子句和 WHEN NOT MATCHED 子句的情况下有效,因此也适用于这种情况。示例用法。

CREATE TABLE #A(
 [id] [int] NOT NULL PRIMARY KEY CLUSTERED,
 [C] [varchar](200) NOT NULL)


    MERGE #A AS target
    USING (SELECT 3, 'C') AS source (id, C)
    ON (target.id = source.id)
    /*Uncomment for Upsert Semantics
       WHEN MATCHED THEN 
        UPDATE SET C = source.C */
    WHEN NOT MATCHED THEN    
        INSERT (id, C)
        VALUES (source.id, source.C);

就执行成本而言,当要完成插入时,两者看起来大致相等......

Link to plan images for first run

但是在第二次运行时,当没有插入时,马修的答案看起来成本更低。我不确定是否有办法改善这一点。

Link to plan images for second run

测试脚本

select * 
into #testtable
from master.dbo.spt_values

CREATE UNIQUE CLUSTERED INDEX [ix] ON #testtable([type] ASC,[number] ASC,[name] ASC)


declare @name nvarchar(35)= 'zzz'
declare @number int = 50
declare @type nchar(3) = 'A'
declare @low int
declare @high int
declare @status int = 0;



MERGE #testtable AS target
USING (SELECT @name, @number, @type, @low, @high, @status) AS source (name, number, [type], low, high, [status])
ON (target.[type] = source.[type] AND target.[number] = source.[number] and target.[name] = source.[name] )
WHEN NOT MATCHED THEN    
INSERT (name, number, [type], low, high, [status])
VALUES (source.name, source.number, source.[type], source.low, source.high, source.[status]);

set @name = 'yyy'

IF NOT EXISTS 
    (SELECT *
    FROM #testtable
    WHERE [type] = @type AND [number] = @number and name = @name)
    BEGIN
INSERT INTO #testtable
(name, number, [type], low, high, [status])
VALUES (@name, @number, @type, @low, @high, @status);
END

【讨论】:

  • 我实际上不确定我当前使用的是哪个版本。合并将如何工作?
  • @Mega - 我已经用MERGE 可用于这种情况的示例更新了我的答案。我将检查执行统计信息,看看这两种方法之间是否有任何区别。
  • 问题是正确性,而不是性能。 IF NOT EXISTS (SELECT ...) INSERT 会在负载下导致重复错误,保证。
  • @Remus - 好点。谢谢,我根本没有考虑过这方面。
  • @RemusRusanu - 顺便说一句,According to this article Merge 在这方面仍然存在问题,除非使用 HOLDLOCK
【解决方案2】:
IF NOT EXISTS 
    (SELECT {Columns} 
    FROM {Table} 
    WHERE {Column1 = SomeValue AND Column2 = SomeOtherVale AND ...}) 
INSERT INTO {Table} {Values}

【讨论】:

  • -1 因为这是在高负载下获得重复错误的可靠方法。 SELECT 和 INSERT 在不同的时间运行,没有什么可以阻止两个并发线程尝试插入相同的值。 Martin 发布的 MERGE 是一个合适的解决方案
【解决方案3】:

简而言之,您需要一个保证能够返回一行的表:

Insert dbo.Table (Col1, Col2, Col3....
Select 'Value1', 'Value2', 'Value3',....
From Information_Schema.Tables
Where Table_Schema = 'dbo'
    And Table_Name = 'Table'
    And Not Exists  (
                    Select 1
                    From dbo.Table
                    Where Col1 = 'Foo'
                        And Col2 = 'Bar'
                        And ....
                    )

我在野外也看到过这种变化:

Insert Table (Col1, Col2, Col3....
Select 'Value1', 'Value2', 'Value3'....
From    (
        Select 1 As Num
        ) As Z
Where Not Exists    (
                    Select 1
                    From Table
                    Where Col1 = Foo
                        And Col2 = Bar
                        And ....
                    ) 

【讨论】:

    【解决方案4】:

    我必须投票支持添加CONSTRAINT。这是最简单和最可靠的答案。我的意思是,看看其他答案有多复杂,我会说它们更难正确(并保持正确)。

    缺点:[1] 阅读代码并不能明显看出数据库中强制执行的唯一性 [2] 客户端代码必须知道才能捕获异常。换句话说,追随你的人可能想知道“这是怎么做到的?”

    除此之外:我曾经担心抛出/捕获异常会影响性能,但我做了一些测试(在 SQL Server 2005 上),结果并不显着。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-22
      • 2016-05-07
      • 2013-06-14
      • 1970-01-01
      • 2018-04-02
      • 1970-01-01
      • 2013-05-03
      • 1970-01-01
      相关资源
      最近更新 更多