Sql 如果在插入、更新、删除之前存在,请进行优化
通常情况下,您需要根据某些条件执行INSERT、UPDATE或DELETE语句。我的问题是,在命令之前是否存在对查询addif性能的影响 范例Sql 如果在插入、更新、删除之前存在,请进行优化,sql,sql-server,tsql,optimization,Sql,Sql Server,Tsql,Optimization,通常情况下,您需要根据某些条件执行INSERT、UPDATE或DELETE语句。我的问题是,在命令之前是否存在对查询addif性能的影响 范例 IF EXISTS(SELECT 1 FROM Contacs WHERE [Type] = 1) UPDATE Contacs SET [Deleted] = 1 WHERE [Type] = 1 插入或删除如何?如果存在语句,则执行该语句: IF EXISTS(SELECT 1 FROM mytable WHERE someColumn =
IF EXISTS(SELECT 1 FROM Contacs WHERE [Type] = 1)
UPDATE Contacs SET [Deleted] = 1 WHERE [Type] = 1
插入或删除如何?如果存在
语句,则执行该语句:
IF EXISTS(SELECT 1 FROM mytable WHERE someColumn = someValue)
取决于满足查询的索引。在大多数情况下,您不应该这样做。根据您的事务级别,您已经创建了一个竞争条件,现在在您的示例中,这无关紧要,但是数据可以从第一次选择更改为更新。您所做的只是强迫SQL做更多的工作
最好的确定方法是测试这两个差异,看看哪一个给了您适当的性能。有一点影响,因为您至少在您的示例中进行了两次相同的检查:
IF EXISTS(SELECT 1 FROM Contacs WHERE [Type] = 1)
必须查询,查看是否存在,如果为真,则:
UPDATE Contacs SET [Deleted] = 1 WHERE [Type] = 1
必须查询,看哪一个…无缘无故地检查两次。现在,如果您要查找的条件已被索引,则应该很快,但是对于大型表,您可能会看到一些延迟,因为您正在运行select。是,这将影响性能(性能受影响的程度将受到多个因素的影响)。实际上,您正在“两次”执行相同的查询(在您的示例中)。问问你自己,在你的询问中你是否需要这样的防御,在什么情况下,争吵不会出现?此外,使用update语句,受影响的行可能是确定是否有任何内容已更新的更好方法 如果存在
将基本上进行选择-与更新相同
因此,它将降低性能-如果没有要更新的内容,则您所做的工作与所选内容相同(更新将查询到的行数与所选内容相同),如果有要更新的内容,则您将执行一个不需要的选择。这对于一次更新/删除/插入不有用。
如果在if条件之后有多个运算符,则可能会增加性能。
在最后一种情况下,最好写
update a set .. where ..
if @@rowcount > 0
begin
..
end
你不应该为更新和删除而这样做,因为如果对性能有影响,那么它不是一个积极的影响
对于INSERT
,您的INSERT
可能会引发异常(UNIQUE CONSTRAINT
违例等),在这种情况下,您可能希望使用(如果存在)
)来阻止它,并更优雅地处理它。这在很大程度上重复了前面的五(不,六)(不,七)个答案,但是:
是的,您拥有的IF-EXISTS结构大体上将使数据库所做的工作翻一番。虽然IF-EXISTS在找到第一个匹配行时会“停止”(它不需要找到所有匹配行),但对于更新和删除来说,这仍然是额外的、最终毫无意义的工作
- 如果不存在这样的行,如果存在,将进行完整扫描(表或索引)以确定这一点
- 如果存在一个或多个这样的行,If EXISTS将读取足够多的表/索引以查找第一行,然后UPDATE或DELETE将重新读取该表以再次查找并处理它——并且它将读取表的“其余部分”,以查看是否还有更多的表/索引要处理。(如果索引正确,速度足够快,但仍然如此。)
因此,无论哪种方式,您都将至少读取一次整个表或索引。但是,为什么一开始就要为IF的存在而烦恼呢
UPDATE Contacs SET [Deleted] = 1 WHERE [Type] = 1
或者,无论是否找到要处理的行,类似的删除都可以正常工作。没有行,扫描了表,没有修改,完成了;1+行,扫描表格,应该修改的所有内容,再次执行。一次通过,没有大惊小怪,没有混乱,不必担心“在我的第一次查询和第二次查询之间,数据库是否被另一个用户更改了”
插入是一种可能有用的情况——在添加行之前检查行是否存在,以避免主键或唯一键冲突。当然,您必须担心并发性——如果其他人试图在您的同时添加此行,该怎么办?将这一切包装到单个插入中,将在隐式事务中处理这一切(记住您的ACID属性!):
在不存在的地方插入Contacs(col1、col2等)值(val1、val2等)(从Contacs中选择1,其中col1=val1)
如果@rowcount=0,则
UPDATE … IF (@@ROWCOUNT = 0) INSERT
也不是
模式在高并发性下按预期工作。两者都可能失败。两者都可能经常失败。合并是王道-它能支撑得更好。让我们做一些压力测试,自己看看
以下是我们将使用的表格:
CREATE TABLE dbo.TwoINTs
(
ID INT NOT NULL PRIMARY KEY,
i1 INT NOT NULL ,
i2 INT NOT NULL ,
version ROWVERSION
) ;
GO
INSERT INTO dbo.TwoINTs
( ID, i1, i2 )
VALUES ( 1, 0, 0 ) ;
如果存在(…),则模式在高并发性下经常失败。
让我们使用以下简单逻辑在循环中插入或更新行:如果存在具有给定ID的行,则更新它,否则插入新的行。下面的循环实现了这个逻辑。将其剪切并粘贴到两个选项卡中,在两个选项卡中切换到文本模式,并同时运行它们
-- hit Ctrl+T to execute in text mode
SET NOCOUNT ON ;
DECLARE @ID INT ;
SET @ID = 0 ;
WHILE @ID > -100000
BEGIN ;
SET @ID = ( SELECT MIN(ID)
FROM dbo.TwoINTs
) - 1 ;
BEGIN TRY ;
BEGIN TRANSACTION ;
IF EXISTS ( SELECT *
FROM dbo.TwoINTs
WHERE ID = @ID )
BEGIN ;
UPDATE dbo.TwoINTs
SET i1 = 1
WHERE ID = @ID ;
END ;
ELSE
BEGIN ;
INSERT INTO dbo.TwoINTs
( ID, i1, i2 )
VALUES ( @ID, 0, 0 ) ;
END ;
COMMIT ;
END TRY
BEGIN CATCH ;
ROLLBACK ;
SELECT error_message() ;
END CATCH ;
END ;
当我们在两个选项卡中同时运行这个脚本时,我们将立即在两个选项卡中得到大量主键冲突。这说明了IF-EXISTS模式在高并发性下执行时是多么不可靠
注意:此示例还表明,如果我们在并发下执行,则使用SELECT MAX(ID)+1或SELECT MIN(ID)-1作为下一个可用的唯一值是不安全的。我不完全确定,但我得到的印象是,这个问题实际上是关于upsert的,这是以下原子操作:
- 如果源和目标中都存在该行,
更新目标李>
- 如果该行仅存在于源中,
将该行插入目标中李>
- (可选)如果行存在于目标中,但不存在于源中,
DELETE
t
CREATE TABLE dbo.TwoINTs
(
ID INT NOT NULL PRIMARY KEY,
i1 INT NOT NULL ,
i2 INT NOT NULL ,
version ROWVERSION
) ;
GO
INSERT INTO dbo.TwoINTs
( ID, i1, i2 )
VALUES ( 1, 0, 0 ) ;
-- hit Ctrl+T to execute in text mode
SET NOCOUNT ON ;
DECLARE @ID INT ;
SET @ID = 0 ;
WHILE @ID > -100000
BEGIN ;
SET @ID = ( SELECT MIN(ID)
FROM dbo.TwoINTs
) - 1 ;
BEGIN TRY ;
BEGIN TRANSACTION ;
IF EXISTS ( SELECT *
FROM dbo.TwoINTs
WHERE ID = @ID )
BEGIN ;
UPDATE dbo.TwoINTs
SET i1 = 1
WHERE ID = @ID ;
END ;
ELSE
BEGIN ;
INSERT INTO dbo.TwoINTs
( ID, i1, i2 )
VALUES ( @ID, 0, 0 ) ;
END ;
COMMIT ;
END TRY
BEGIN CATCH ;
ROLLBACK ;
SELECT error_message() ;
END CATCH ;
END ;
-- For each row in source
IF EXISTS(<target_expression>)
IF @delete_flag = 1
DELETE <target_expression>
ELSE
UPDATE target
SET <target_columns> = <source_values>
WHERE <target_expression>
ELSE
INSERT target (<target_columns>)
VALUES (<source_values>)
-- For each row in source
BEGIN TRAN
UPDATE target
SET <target_columns> = <source_values>
WHERE <target_expression>
IF (@@ROWCOUNT = 0)
INSERT target (<target_columns>)
VALUES (<source_values>)
COMMIT
BEGIN TRAN
INSERT target (<target_columns>)
SELECT <source_columns> FROM source s
WHERE s.id NOT IN (SELECT id FROM target)
UPDATE t SET <target_columns> = <source_columns>
FROM target t
INNER JOIN source s ON t.d = s.id
DELETE t
FROM target t
WHERE t.id NOT IN (SELECT id FROM source)
COMMIT
MERGE target
USING source ON target.id = source.id
WHEN MATCHED THEN UPDATE <target_columns> = <source_columns>
WHEN NOT MATCHED THEN INSERT (<target_columns>) VALUES (<source_columns>)
WHEN NOT MATCHED BY SOURCE THEN DELETE;
IF EXISTS....UPDATE