Microsoft SQL Serverにテーブルがあります。更新が必要な場合と挿入が必要な場合があります。 2つのストアドプロシージャを記述できます。
InsertNewPerson
UpdatePertsonById
しかし、私は両方を行う1つのストアドプロシージャ(SetPerson
)を作成することを考えていました(ID
がある場合、それはupdate
操作です。それ以外の場合はinsert
)。
1つのストアドプロシージャ(1つだけを維持する)を作成する必要がありますか、それとも2つの異なるストアドプロシージャを作成する必要がありますか?
私が理解している限り、ここで実際にUPSERT
について話しているのではなく、1つのストアドプロシージャで2つの異なるCRUD操作を組み合わせているだけです。
CREATE PROC InsertOrUpdateYourTable @Id int = NULL OUTPUT,
@Foo INT,
@Bar VARCHAR(10)
AS
IF @Id IS NULL
BEGIN
INSERT INTO YourTable
(Foo,
Bar)
VALUES (@Foo,
@Bar)
SET @Id = SCOPE_IDENTITY()
END
ELSE
BEGIN
UPDATE YourTable
SET Foo = @Foo,
Bar = @Bar
WHERE Id = @Id
END
これで私が目にする利点は、テーブル構造が変更された場合に2つの個別のパラメーターリストを維持する必要がないことです。欠点は、単一のストアドプロシージャに2つの責任があり、理解がやや難しいことです。
私は通常、それらを2つのストアドプロシージャに分離することを選択します。
RE:「アップサートの外観について詳しく教えてください」
CREATE PROC UpsertYourTable
@Id int,
@Foo int,
@Bar varchar(10)
AS
MERGE YourTable WITH (HOLDLOCK) AS T
USING ( VALUES ( @Id, @Foo, @Bar ) )
AS source ( Id, Foo, Bar)
ON ( T.Id = source.Id )
WHEN MATCHED
THEN
UPDATE SET
Foo = source.Foo ,
Bar = source.Bar
WHEN NOT MATCHED
THEN
INSERT (Id, Foo , Bar)
VALUES
(@Id, @Foo , @Bar);
これは、Id
がIDENTITY
列ではなくなったと想定しています。 HOLDLOCK
を使用する理由 ここで説明します 。
1つのプロシージャを使用する場合は、 MERGEステートメント を使用できます。
マージコードの例:
create table testo(Id int identity(1,1) NOT NULL, somechar char(1), someint int, AddedTime datetime2(0), LastModifiedTime datetime2(0));
alter table testo add constraint [PK_testo] primary key(Id); --Clustered index on target table.
--No index necessary on the source 'table' because we're creating it from parameters.
declare @Id int = 1;
declare @somechar char(1) = 'A';
declare @someint int = 42;
declare @results table (DMLAction sysname, Id int, somechar char(1), someint int );
MERGE dbo.testo AS Target
USING
(
SELECT
@Id as Id,
@somechar as somechar,
@someint as someint
) AS SOURCE
ON
TARGET.Id = SOURCE.Id
WHEN MATCHED
THEN UPDATE SET
TARGET.somechar = SOURCE.somechar,
TARGET.someint = SOURCE.someint,
TARGET.LastModifiedTime = CURRENT_TIMESTAMP
WHEN NOT MATCHED BY TARGET
THEN INSERT
(
somechar,
someint,
AddedTime,
LastModifiedTime
)
VALUES
(
SOURCE.somechar,
SOURCE.someint,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
)
OUTPUT
$action,
inserted.Id,
inserted.somechar,
inserted.someint
INTO
@results;
select * from @results;
最大の欠点は、パフォーマンスが低下することです(ただし、 賢明なインデックス付けにより、パフォーマンスの問題を軽減できます )。それを打ち消す、いくつかの利点があります。
最初の利点は 監査ははるかに完全です です。必要な場合にSOURCEおよびTARGETレコードの値にアクセスできるためです。これは、INSERTまたはUPDATEのOUTPUT句からは取得できないものです。
このアプローチの2番目の利点は、個別のパラメーター(この簡単な例で行ったような)を使用する代わりに、データレイヤーに table-valued parameter を作成し、それをSOURCEテーブルとして使用できることです。つまり、理論的には、同じ呼び出しで複数のレコードを挿入/更新できます。確かに、同じプロシージャまたは2つの別々のストアドプロシージャで別々のINSERTステートメントとUPDATEステートメントを使用しても同じことができますが、これにより、2つではなく1つのT-SQLステートメントを維持できます。
最後に、必要な努力をしたい場合は、MergeのOUTPUT句の結果(上記のサンプルの@results)を取得し、それを.NET(またはアプリケーションレイヤーが何であれ)にフィードバックして、テーブルの結果。これは簡単なことではありませんが、もう一度Getを呼び出す必要はありません。また、複数のレコードを同時に挿入する場合、SCOPE_IDENTITY()を介してID列の結果を取得しようとする必要はありません。挿入された疑似テーブルには、すでに各レコードのID列の値が含まれています。
繰り返しになりますが、ほとんどの利点は、別々の挿入ステートメントと更新ステートメントで複製できることです。そのため、これは主にMERGEのパフォーマンスコストと、2箇所ではなく1箇所で正しく取得する必要があり、別々に行う必要がないことによるメンテナンスの利点に要約されます。ビジネスレイヤーまたはデータレイヤーにロジックを挿入/更新します。