sql >> データベース >  >> RDS >> Sqlserver

SQL INSERT、ただし重複は避ける

    編集 :競合状態 を防ぐため 並行環境では、WITH (UPDLOCK) を使用します 相関サブクエリまたは EXCEPT で 'd SELECT .以下に記述したテスト スクリプトは、現在の接続でのみ表示される一時テーブルを使用するため、これを必要としませんが、実際の環境では、ユーザー テーブルに対して操作する必要があります。

    MERGE UPDLOCK は必要ありません .

    mcl の回答 re:unique index &let the database throw an error に触発されて、条件付き挿入try/catch .

    結果は、try/catch よりも条件付き挿入をサポートしているように見えますが、YMMV.これは非常に単純なシナリオ (1 つの列、小さなテーブルなど) で、1 台のマシンで実行されます。

    結果は次のとおりです (SQL Server 2008、ビルド 10.0.1600.2):

    duplicates (short table)    
      try/catch:                14440 milliseconds / 100000 inserts
      conditional insert:        2983 milliseconds / 100000 inserts
      except:                    2966 milliseconds / 100000 inserts
      merge:                     2983 milliseconds / 100000 inserts
    
    uniques
      try/catch:                 3920 milliseconds / 100000 inserts
      conditional insert:        3860 milliseconds / 100000 inserts
      except:                    3873 milliseconds / 100000 inserts
      merge:                     3890 milliseconds / 100000 inserts
    
      straight insert:           3173 milliseconds / 100000 inserts
    
    duplicates (tall table)
      try/catch:                14436 milliseconds / 100000 inserts
      conditional insert:        3063 milliseconds / 100000 inserts
      except:                    3063 milliseconds / 100000 inserts
      merge:                     3030 milliseconds / 100000 inserts
    

    ユニークな挿入物でも、わずかに 条件付き挿入よりも try/catch のオーバーヘッドが大きくなります。これはバージョンやCPU、コア数などで違うのかな。

    IF をベンチマークしませんでした 条件付き挿入、ただ WHERE . IF を想定しています a) 2 つのステートメントがあり、b) トランザクションで 2 つのステートメントをラップし、分離レベルをシリアライズ可能 (!) に設定する必要があるため、さまざまな場合はオーバーヘッドが大きくなります。誰かが欲しかった場合 これをテストするには、一時テーブルを通常のユーザー テーブルに変更する必要があります (シリアル化可能はローカル一時テーブルには適用されません)。

    スクリプトは次のとおりです:

    -- tested on SQL 2008.
    -- to run on SQL 2005, comment out the statements using MERGE
    set nocount on
    
    if object_id('tempdb..#temp') is not null drop table #temp
    create table #temp (col1 int primary key)
    go
    
    -------------------------------------------------------
    
    -- duplicate insert test against a table w/ 1 record
    
    -------------------------------------------------------
    
    insert #temp values (1)
    go
    
    declare @x int, @y int, @now datetime, @duration int
    select @x = 1, @y = 0, @now = getdate()
    while @y < 100000 begin
      set @y = @y+1
      begin try 
        insert #temp select @x
      end try
      begin catch end catch
    end
    set @duration = datediff(ms,@now,getdate())
    raiserror('duplicates (short table), try/catch: %i milliseconds / %i inserts',-1,-1,@duration,@y) with nowait
    go
    
    declare @x int, @y int, @now datetime, @duration int
    select @x = 1, @y = 0, @now = getdate()
    while @y < 100000 begin
      set @y = @y+1
      insert #temp select @x where not exists (select * from #temp where col1 = @x)
    end
    set @duration = datediff(ms,@now,getdate())
    raiserror('duplicates (short table), conditional insert: %i milliseconds / %i inserts',-1,-1,@duration, @y) with nowait
    go
    
    declare @x int, @y int, @now datetime, @duration int
    select @x = 1, @y = 0, @now = getdate()
    while @y < 100000 begin
      set @y = @y+1
      insert #temp select @x except select col1 from #temp
    end
    set @duration = datediff(ms,@now,getdate())
    raiserror('duplicates (short table), except: %i milliseconds / %i inserts',-1,-1,@duration, @y) with nowait
    go
    
    -- comment this batch out for SQL 2005
    declare @x int, @y int, @now datetime, @duration int
    select @x = 1, @y = 0, @now = getdate()
    while @y < 100000 begin
      set @y = @y+1
      merge #temp t using (select @x) s (col1) on t.col1 = s.col1 when not matched by target then insert values (col1);
    end
    set @duration = datediff(ms,@now,getdate())
    raiserror('duplicates (short table), merge: %i milliseconds / %i inserts',-1,-1,@duration, @y) with nowait
    go
    
    -------------------------------------------------------
    
    -- unique insert test against an initially empty table
    
    -------------------------------------------------------
    
    truncate table #temp
    declare @x int, @now datetime, @duration int
    select @x = 0, @now = getdate()
    while @x < 100000 begin
      set @x = @x+1
      insert #temp select @x
    end
    set @duration = datediff(ms,@now,getdate())
    raiserror('uniques, straight insert: %i milliseconds / %i inserts',-1,-1,@duration, @x) with nowait
    go
    
    truncate table #temp
    declare @x int, @now datetime, @duration int
    select @x = 0, @now = getdate()
    while @x < 100000 begin
      set @x = @x+1
      begin try 
        insert #temp select @x
      end try
      begin catch end catch
    end
    set @duration = datediff(ms,@now,getdate())
    raiserror('uniques, try/catch: %i milliseconds / %i inserts',-1,-1,@duration, @x) with nowait
    go
    
    truncate table #temp
    declare @x int, @now datetime, @duration int
    select @x = 0, @now = getdate()
    while @x < 100000 begin
      set @x = @x+1
      insert #temp select @x where not exists (select * from #temp where col1 = @x)
    end
    set @duration = datediff(ms,@now,getdate())
    raiserror('uniques, conditional insert: %i milliseconds / %i inserts',-1,-1,@duration, @x) with nowait
    go
    
    truncate table #temp
    declare @x int, @now datetime, @duration int
    select @x = 0, @now = getdate()
    while @x < 100000 begin
      set @x = @x+1
      insert #temp select @x except select col1 from #temp
    end
    set @duration = datediff(ms,@now,getdate())
    raiserror('uniques, except: %i milliseconds / %i inserts',-1,-1,@duration, @x) with nowait
    go
    
    -- comment this batch out for SQL 2005
    truncate table #temp
    declare @x int, @now datetime, @duration int
    select @x = 1, @now = getdate()
    while @x < 100000 begin
      set @x = @x+1
      merge #temp t using (select @x) s (col1) on t.col1 = s.col1 when not matched by target then insert values (col1);
    end
    set @duration = datediff(ms,@now,getdate())
    raiserror('uniques, merge: %i milliseconds / %i inserts',-1,-1,@duration, @x) with nowait
    go
    
    -------------------------------------------------------
    
    -- duplicate insert test against a table w/ 100000 records
    
    -------------------------------------------------------
    
    declare @x int, @y int, @now datetime, @duration int
    select @x = 1, @y = 0, @now = getdate()
    while @y < 100000 begin
      set @y = @y+1
      begin try 
        insert #temp select @x
      end try
      begin catch end catch
    end
    set @duration = datediff(ms,@now,getdate())
    raiserror('duplicates (tall table), try/catch: %i milliseconds / %i inserts',-1,-1,@duration,@y) with nowait
    go
    
    declare @x int, @y int, @now datetime, @duration int
    select @x = 1, @y = 0, @now = getdate()
    while @y < 100000 begin
      set @y = @y+1
      insert #temp select @x where not exists (select * from #temp where col1 = @x)
    end
    set @duration = datediff(ms,@now,getdate())
    raiserror('duplicates (tall table), conditional insert: %i milliseconds / %i inserts',-1,-1,@duration, @y) with nowait
    go
    
    declare @x int, @y int, @now datetime, @duration int
    select @x = 1, @y = 0, @now = getdate()
    while @y < 100000 begin
      set @y = @y+1
      insert #temp select @x except select col1 from #temp
    end
    set @duration = datediff(ms,@now,getdate())
    raiserror('duplicates (tall table), except: %i milliseconds / %i inserts',-1,-1,@duration, @y) with nowait
    go
    
    -- comment this batch out for SQL 2005
    declare @x int, @y int, @now datetime, @duration int
    select @x = 1, @y = 0, @now = getdate()
    while @y < 100000 begin
      set @y = @y+1
      merge #temp t using (select @x) s (col1) on t.col1 = s.col1 when not matched by target then insert values (col1);
    end
    set @duration = datediff(ms,@now,getdate())
    raiserror('duplicates (tall table), merge: %i milliseconds / %i inserts',-1,-1,@duration, @y) with nowait
    go
    


    1. ハイフンを無視するMySQL検索

    2. Mysql前のレコードの値でレコードを更新する

    3. MySQLで外部キー列の名前を変更する

    4. JDBCResultSetのオブジェクトへのマッピング