Chr*_*ore 564 sql database sql-server insert upsert
假设一个表结构MyTable(KEY, datafield1, datafield2...)
.
通常我想要更新现有记录,或者如果新记录不存在则插入新记录.
实质上:
IF (key exists)
run update command
ELSE
run insert command
Run Code Online (Sandbox Code Playgroud)
写这个的最佳表现方式是什么?
Kei*_*ith 370
请参阅我之前非常相似的问题的详细答案
@Beau Crawford在SQL 2005及以下版本中是一个好方法,但是如果你授予代表它应该去找第一个人来做它.唯一的问题是,对于插入,它仍然是两个IO操作.
MS Sql2008 merge
从SQL:2003标准中引入:
merge tablename with(HOLDLOCK) as target
using (values ('new value', 'different value'))
as source (field1, field2)
on target.idfield = 7
when matched then
update
set field1 = source.field1,
field2 = source.field2,
...
when not matched then
insert ( idfield, field1, field2, ... )
values ( 7, source.field1, source.field2, ... )
Run Code Online (Sandbox Code Playgroud)
现在它真的只是一个IO操作,但可怕的代码:-(
aku*_*aku 354
不要忘记交易.性能很好,但简单(IF EXISTS ..)方法非常危险.
当多个线程尝试执行插入或更新时,您可以轻松地获得主键冲突.
@Beau Crawford和@Esteban提供的解决方案显示了一般性的想法,但容易出错.
为了避免死锁和PK违规,您可以使用以下内容:
begin tran
if exists (select * from table with (updlock,serializable) where key = @key)
begin
update table set ...
where key = @key
end
else
begin
insert into table (key, ...)
values (@key, ...)
end
commit tran
Run Code Online (Sandbox Code Playgroud)
要么
begin tran
update table with (serializable) set ...
where key = @key
if @@rowcount = 0
begin
insert into table (key, ...) values (@key,..)
end
commit tran
Run Code Online (Sandbox Code Playgroud)
小智 162
做一个UPSERT:
UPDATE MyTable SET FieldA=@FieldA WHERE Key=@Key IF @@ROWCOUNT = 0 INSERT INTO MyTable (FieldA) VALUES (@FieldA)
http://en.wikipedia.org/wiki/Upsert
Aar*_*and 86
很多人会建议你使用MERGE
,但我提醒你不要这样做.默认情况下,它不会保护您不受多个语句的并发和竞争条件的影响,但它确实会引入其他危险:
http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/
即使使用这种"更简单"的语法,我仍然更喜欢这种方法(为简洁省略了错误处理):
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE dbo.table SET ... WHERE PK = @PK;
IF @@ROWCOUNT = 0
BEGIN
INSERT dbo.table(PK, ...) SELECT @PK, ...;
END
COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)
很多人会这样建议:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
IF EXISTS (SELECT 1 FROM dbo.table WHERE PK = @PK)
BEGIN
UPDATE ...
END
ELSE
INSERT ...
END
COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)
但所有这一切都可以确保您可能需要两次读取表以找到要更新的行.在第一个示例中,您只需要找到一次行.(在这两种情况下,如果从初始读取中找不到行,则会发生插入.)
其他人会建议这样:
BEGIN TRY
INSERT ...
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 2627
UPDATE ...
END CATCH
Run Code Online (Sandbox Code Playgroud)
但是,如果除了在几乎每个插入失败的罕见情况下,除了让SQL Server捕获您可能首先阻止的异常之外没有其他原因,这是有问题的.我在这里证明了这一点:
Est*_*aya 57
IF EXISTS (SELECT * FROM [Table] WHERE ID = rowID)
UPDATE [Table] SET propertyOne = propOne, property2 . . .
ELSE
INSERT INTO [Table] (propOne, propTwo . . .)
Run Code Online (Sandbox Code Playgroud)
编辑:
唉,即使对我自己有害,我必须承认,没有选择的解决方案似乎更好,因为他们只需少一步即可完成任务.
Eri*_*nau 37
如果要一次UPSERT多个记录,可以使用ANSI SQL:2003 DML语句MERGE.
MERGE INTO table_name WITH (HOLDLOCK) USING table_name ON (condition)
WHEN MATCHED THEN UPDATE SET column1 = value1 [, column2 = value2 ...]
WHEN NOT MATCHED THEN INSERT (column1 [, column2 ...]) VALUES (value1 [, value2 ...])
Run Code Online (Sandbox Code Playgroud)
小智 10
虽然对此发表评论已经很晚了,但我想用MERGE添加一个更完整的例子.
此类Insert + Update语句通常称为"Upsert"语句,可以使用SQL Server中的MERGE实现.
这里给出了一个很好的例子:http: //weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
以上解释了锁定和并发方案.
我将引用相同的参考:
ALTER PROCEDURE dbo.Merge_Foo2
@ID int
AS
SET NOCOUNT, XACT_ABORT ON;
MERGE dbo.Foo2 WITH (HOLDLOCK) AS f
USING (SELECT @ID AS ID) AS new_foo
ON f.ID = new_foo.ID
WHEN MATCHED THEN
UPDATE
SET f.UpdateSpid = @@SPID,
UpdateTime = SYSDATETIME()
WHEN NOT MATCHED THEN
INSERT
(
ID,
InsertSpid,
InsertTime
)
VALUES
(
new_foo.ID,
@@SPID,
SYSDATETIME()
);
RETURN @@ERROR;
Run Code Online (Sandbox Code Playgroud)
小智 8
/*
CREATE TABLE ApplicationsDesSocietes (
id INT IDENTITY(0,1) NOT NULL,
applicationId INT NOT NULL,
societeId INT NOT NULL,
suppression BIT NULL,
CONSTRAINT PK_APPLICATIONSDESSOCIETES PRIMARY KEY (id)
)
GO
--*/
DECLARE @applicationId INT = 81, @societeId INT = 43, @suppression BIT = 0
MERGE dbo.ApplicationsDesSocietes WITH (HOLDLOCK) AS target
--set the SOURCE table one row
USING (VALUES (@applicationId, @societeId, @suppression))
AS source (applicationId, societeId, suppression)
--here goes the ON join condition
ON target.applicationId = source.applicationId and target.societeId = source.societeId
WHEN MATCHED THEN
UPDATE
--place your list of SET here
SET target.suppression = source.suppression
WHEN NOT MATCHED THEN
--insert a new line with the SOURCE table one row
INSERT (applicationId, societeId, suppression)
VALUES (source.applicationId, source.societeId, source.suppression);
GO
Run Code Online (Sandbox Code Playgroud)
根据您的需要替换表和字段名称.注意使用ON条件.然后在DECLARE行上为变量设置适当的值(和类型).
干杯.
小智 7
您可以使用MERGE
Statement,此语句用于插入数据(如果不存在)或更新(如果存在).
MERGE INTO Employee AS e
using EmployeeUpdate AS eu
ON e.EmployeeID = eu.EmployeeID`
Run Code Online (Sandbox Code Playgroud)
如果要先执行UPDATE if-no-rows-updated然后执行INSERT路由,请考虑先执行INSERT以防止出现争用情况(假设中间没有DELETE)
INSERT INTO MyTable (Key, FieldA)
SELECT @Key, @FieldA
WHERE NOT EXISTS
(
SELECT *
FROM MyTable
WHERE Key = @Key
)
IF @@ROWCOUNT = 0
BEGIN
UPDATE MyTable
SET FieldA=@FieldA
WHERE Key=@Key
IF @@ROWCOUNT = 0
... record was deleted, consider looping to re-run the INSERT, or RAISERROR ...
END
Run Code Online (Sandbox Code Playgroud)
除了避免出现竞争状况外,如果在大多数情况下该记录将已经存在,那么这将导致INSERT失败,从而浪费CPU。
从SQL2008起,最好使用MERGE。
这取决于使用模式。人们必须着眼于使用大图,而不要迷失在细节中。例如,如果使用模式是在创建记录后 99% 更新,则“UPSERT”是最佳解决方案。
在第一次插入(命中)之后,它将是所有单个语句更新,没有 ifs 或 buts。插入的“where”条件是必要的,否则它会插入重复项,并且您不想处理锁定。
UPDATE <tableName> SET <field>=@field WHERE key=@key;
IF @@ROWCOUNT = 0
BEGIN
INSERT INTO <tableName> (field)
SELECT @field
WHERE NOT EXISTS (select * from tableName where key = @key);
END
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
601158 次 |
最近记录: |