SQL Server中事務transaction如果沒寫在try catch中,就算中間語句報錯還是會提交


假如我們數據庫中有兩張表Person和Book

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

 

Book表:

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表是一對多關系,一個Person可以有多個Book,所以Book表的PersonCode列是外鍵,指向Person表的Code列,並為強制約束,也就是說Book表的PersonCode列的值,只能是Person表的Code列值,否則SQL Server會報錯:

 

現在我們執行下面語句給兩張表插入數據,我們將插入Person表和Book表的兩個Insert語句寫在了個事務transaction中,按道理其中一個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')--由於Book表的[PersonCode]列值'XXX'在Person表的[Code]列中不存在,所以整個INSERT INTO [dbo].[Book]語句會報錯不會執行

COMMIT

由於值"XXX"在Person表的[Name]列中不存在,所以INSERT INTO [dbo].[Book]語句報錯沒有執行,但是我們意外地發現INSERT INTO Person卻隨着事務Commit一起提交了。。。Person表的數據被成功插入了。。。

(1 行受影響)
消息 547,級別 16,狀態 0,第 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 catch中:

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')--由於Book表的[PersonCode]列值'XXX'在Person表的[Code]列中不存在,所以整個INSERT INTO [dbo].[Book]語句會報錯不會執行


    COMMIT

END TRY
BEGIN CATCH

    ROLLBACK 

END CATCH

運行后,我們再查詢Person表的數據:

查詢Book表數據:

我們可以看到這次就和我們的預期一致了,在INSERT INTO [dbo].[Book]語句報錯后,后面的Commit語句沒有執行,執行了catch中的Rollback,最后兩張表的數據都沒有插入數據庫。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM