単一のクエリを使用して挿入または更新する方法は?


25

私は、主キーと自動インクリメントされた名前の列IDを持つテーブルテストを持っています。レコードがない場合にのみ、新しいレコードを挿入します。たとえば、

入力はid = 30122および名前= johnです

ID 30122のレコードがある場合、名前列をjohnに更新し、レコードがない場合、新しいレコードを挿入します。

私は次のような2つのクエリを使用して行うことができます

select * from test where id=30122

いくつかのレコードがある場合は、使用できます update test set name='john' where id=3012

またはレコードがない場合は、使用できます

insert into test(name) values('john')

しかし、私は単一のクエリを使用したいですか?

誰かがその可能性を伝えることができますか?


1
But I wanted to use single query?どうして?
アーロンバートランド

私はそれがなぜ2つのクエリを使用して、単一のクエリを使用して行うことができる場合、私は、DB 2 times.Soをヒットする必要があり、その後2 quriesを使用する場合は@AaronBertrand私のバックエンドはjava.Soを使用して開発された
SpringLearner

1
Javaは、ストアドプロシージャ、またはデータベースへのヒットを1つだけ必要とする2つのステートメントを含む単一のバッチをサポートしていませんか?
アーロンバートランド

@AaronBertrandは、SQL Server 2008以降でこれをどのように処理するかの例を与えることができますか?
eaglei22

1
@ eaglei22以下のvijaypの回答の2番目の例を使用します。MERGESQL Server 2019であっても、どのバージョンでも選択しません。その背景については、こちらをご覧ください
アーロンバートランド

回答:


41

これを試すことができます

IF EXISTS(select * from test where id=30122)
   update test set name='john' where id=3012
ELSE
   insert into test(name) values('john');

より良いパフォーマンスのためのその他のアプローチは

update test set name='john' where id=3012
IF @@ROWCOUNT=0
   insert into test(name) values('john');

また、この悪い習慣を読んでスキーマ接頭辞を開始します


4
最初の例は無駄であり、しばしばデッドロックを引き起こす可能性があります-私はそれをまったく提案しません。
アーロンバートランド

@AaronBertrandの手入れは?ありがとう
-Hexo

5
@SlapY確かに、最初の例では、「SQL Server、このIDを持つ行がありますか?」と言っています。SQL Serverは、おそらくスキャンを使用して行を見つけてから、答えを返します。「ユーザー、なぜ、そのIDの行がありますか!」次に、「さて、SQL Server、もう一度その行を見つけに行きますが、今回は更新してください」と言います。シークまたはスキャンを2回実行するのは無駄だと思いますか?他のユーザーが行の存在についてSQL Serverに同じ質問をした場合、それについて何かを行う前に何が起こるか想像できますか?
アーロンバートランド

おかげで、なぜ最初のものがデッドロックの脅威にさらされているのかわかりません。両方とも、フルロックで実行しない場合にインターセプトできる複数のステートメントで構成されます。私が間違っている?
ヘキソ

2
@ 0x25b3 1つはデッドロックの脅威にさらされているのではなく、もう1つはデッドロックの脅威にさらされているわけではありません。あなたはどちらの場合も、完全かつ適切な取引でラッピングする必要がありますが、人々はそうではないので...
アーロン・ベルトラン

17

SQL Server 2008以降を想定すると、次を使用できますMERGE

CREATE TABLE dbo.Test
(
    id integer NOT NULL,
    name varchar(30) NULL,

    CONSTRAINT PK_dbo_Test__id
        PRIMARY KEY CLUSTERED (id)
);

問い合わせ

MERGE dbo.Test WITH (SERIALIZABLE) AS T
USING (VALUES (3012, 'john')) AS U (id, name)
    ON U.id = T.id
WHEN MATCHED THEN 
    UPDATE SET T.name = U.name
WHEN NOT MATCHED THEN
    INSERT (id, name) 
    VALUES (U.id, U.name);

SERIALIZABLEヒントはのために必要とされる高い同時実行の下で正しく動作します

Michael J. Swartによる一般的な方法の比較については、こちらをご覧ください。

ミスバスティング:同時更新/挿入ソリューション



そこの神話へのリンクは素晴らしい。良いですね!
JonnyRaa
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.