代码之家  ›  专栏  ›  技术社区  ›  Matt Anxo P

仅当表中不存在记录时插入记录

  •  30
  • Matt Anxo P  · 技术社区  · 14 年前

    我想知道是否有一种方法可以将一条记录插入到一个表中,前提是该表不包含该记录?

    是否有这样的查询,或者我需要一个存储过程?

    4 回复  |  直到 6 年前
        1
  •  25
  •   Martin Smith    14 年前

    您不必说什么版本的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
    
        2
  •  12
  •   Matthew Jones    14 年前
    IF NOT EXISTS 
        (SELECT {Columns} 
        FROM {Table} 
        WHERE {Column1 = SomeValue AND Column2 = SomeOtherVale AND ...}) 
    INSERT INTO {Table} {Values}
    
        3
  •  1
  •   Thomas    14 年前

    简言之,您需要一个保证可以返回一行的表:

    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
  •  0
  •   egrunin    14 年前

    我必须投赞成票 CONSTRAINT . 这是最简单、最有力的答案。我的意思是,看看其他答案有多复杂,我会说他们很难正确(并且保持正确)。

    缺点是[1]从代码中不明显地看出,在数据库[2]中强制唯一性客户端代码必须知道才能捕获异常。换句话说,跟在你后面的人可能会想,“这是怎么工作的?”

    撇开这一点:我曾经担心抛出/捕获异常会影响性能,但我做了一些测试(在SQL Server 2005上),结果并不重要。