假如我們數據庫中有兩張表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,最后兩張表的數據都沒有插入數據庫。