1. ホーム
  2. データベース
  3. Mysql

SQL Server のトランザクションは、try キャッチに記述しなければ、中間ステートメントがエラーを報告してもコミットされます。

2022-01-21 19:18:56

データベースにPersonとBookの2つのテーブルがある場合

Personテーブル。

CREATE TABLE [dbo]. [Person](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Code] [nvarchar](50) NULL,
    [Name] [nvarchar](50) NULL,
    [CreateTime] [datetime] NULL,
    [UpdateTime] [datetime] NULL,
 CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo]. [Person] ADD CONSTRAINT [DF_Person_CreateTime] DEFAULT (getdate()) FOR [CreateTime]
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_Person] ON [dbo]. [Person].
(
    [Code] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

ブックテーブルです。

CREATE TABLE [dbo]. [Book](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [BookCode] [nvarchar](50) NULL,
    [BookName] [nvarchar](50) NULL,
    [PersonCode] [nvarchar](50) NULL,
 CONSTRAINT [PK_Book] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo]. [Book] WITH CHECK ADD CONSTRAINT [FK_Book_Person] FOREIGN KEY([PersonCode])
REFERENCES [dbo]. [Person] ([Code])
ON UPDATE CASCADE
ON DELETE CASCADE
GO

ALTER TABLE [dbo]. [Book] CHECK CONSTRAINT [FK_Book_Person]
GO

PersonテーブルとBookテーブルは1対多の関係で、Personは複数のBookを持つことができるので、BookテーブルのPersonCode列はPersonテーブルのCode列を指す外部キーであり、必須制約であり、BookテーブルのPersonCode列の値はPersonテーブルのCode列の値でなければならないことを示していることがわかるでしょう。そうでない場合、SQL Server は次のようなエラーを報告します。

ここで、2つのテーブルにデータを挿入するために、次のステートメントを実行します。トランザクション トランザクションで Person テーブルと Book テーブルに 2 つの Insert ステートメントを記述しており、1 つの Insert が失敗しても、もう 1 つの Insert は失敗しないという理屈になっています。

BEGIN TRAN

INSERT INTO Person([Code],[Name])
VALUES('P003','Jack')


INSERT INTO [dbo]. [Book]([BookCode],[BookName],[PersonCode])
VALUES
('B001','B001','P003'),
('B002','B002','P003'),
('B003','B003','P003'),
('B004','B004','P003'),
('B005','B005','XXX') - Since the [PersonCode] column value 'XXX' of the Book table does not exist in the [Code] column of the Person table, the entire INSERT INTO [dbo]. [Book] statement will report an error and not execute

COMMIT

Person テーブルの [Name] 列に値 "XXX" が存在しないので、INSERT INTO [dbo].Person テーブルの [Name] 列に値 "XXX" を挿入しました。Book] 文はエラーを報告して実行されませんでしたが、INSERT INTO Person がトランザクション Commit と共にコミットされたことに驚きました。Personテーブルのデータは正常に挿入されました。

(1 row affected)
Message 547, level 16, status 0, line 7
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Book_Person". The conflict occurred in database "TestDB", table "dbo.Person", column 'Code'.
The statement has been terminated.

Personテーブルのデータを問い合わせる。

Bookテーブルのデータを問い合わせる。

これは、INSERT INTO [dbo]. [Book] ステートメントはエラーで実行されませんでしたが、一番下の Commit ステートメントが実行されたため、INSERT INTO [dbo]. [Book]ステートメントは、その後のCommitステートメントの実行を妨げなかったので、INSERT INTO Personステートメントは、トランザクションがデータベースにコミットされたときに有効になった。

ここで、上記のステートメントを以下のように変更し、両方のInsertステートメントをtryキャッチに入れます。

BEGIN TRAN

BEGIN TRY

    INSERT INTO Person([Code],[Name])
    VALUES('P003','Jack')


    INSERT INTO [dbo]. [Book]([BookCode],[BookName],[PersonCode])
    VALUES
    ('B001','B001','P003'),
    ('B002','B002','P003'),
    ('B003','B003','P003'),
    ('B004','B004','P003'),
    ('B005','B005','XXX') - Since the [PersonCode] column value 'XXX' of the Book table does not exist in the [Code] column of the Person table, the entire INSERT INTO [dbo]. [Book] statement will report an error and not execute


    COMMIT

END TRY
BEGIN CATCH

    ROLLBACK 

END CATCH

実行後、次に Person テーブルにデータを問い合わせます。

Bookテーブルのデータを問い合わせる。

今回は予想通り、INSERT INTO [dbo]の後であることがわかります。Book]ステートメントがエラーを報告した後、その後のCommitステートメントは実行されず、Rollback in catchが実行され、最後には両方のテーブルのデータがデータベースに挿入されません。