Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/84.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 如果在插入、更新、删除之前存在,请进行优化_Sql_Sql Server_Tsql_Optimization - Fatal编程技术网

Sql 如果在插入、更新、删除之前存在,请进行优化

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 =

通常情况下,您需要根据某些条件执行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 = 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