【发布时间】:2010-07-16 21:10:02
【问题描述】:
我想知道是否有一种方法可以仅在表中不包含该记录的情况下将记录插入到表中?
是否有可以执行此操作的查询,或者我需要存储过程?
【问题讨论】:
-
您是否考虑过添加唯一性约束?
标签: sql sql-server tsql
我想知道是否有一种方法可以仅在表中不包含该记录的情况下将记录插入到表中?
是否有可以执行此操作的查询,或者我需要存储过程?
【问题讨论】:
标签: sql sql-server tsql
你没有说什么版本的 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
【讨论】:
MERGE 可用于这种情况的示例更新了我的答案。我将检查执行统计信息,看看这两种方法之间是否有任何区别。
IF NOT EXISTS (SELECT ...) INSERT 会在负载下导致重复错误,保证。
Merge 在这方面仍然存在问题,除非使用 HOLDLOCK。
IF NOT EXISTS
(SELECT {Columns}
FROM {Table}
WHERE {Column1 = SomeValue AND Column2 = SomeOtherVale AND ...})
INSERT INTO {Table} {Values}
【讨论】:
简而言之,您需要一个保证能够返回一行的表:
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 ....
)
【讨论】:
我必须投票支持添加CONSTRAINT。这是最简单和最可靠的答案。我的意思是,看看其他答案有多复杂,我会说它们更难正确(并保持正确)。
缺点:[1] 阅读代码并不能明显看出数据库中强制执行的唯一性 [2] 客户端代码必须知道才能捕获异常。换句话说,追随你的人可能想知道“这是怎么做到的?”
除此之外:我曾经担心抛出/捕获异常会影响性能,但我做了一些测试(在 SQL Server 2005 上),结果并不显着。
【讨论】: