I was geeking last weekend, no wait, actually three weeks ago, and then last night and tonight…it doesn’t really matter, with CASCADE DELETES in SQL Server 2008 R2, and was curious about whether or not I could trace Cascade Referential Integrity in SQL Server Profiler.  Do you use Cascade Deletes in your foreign keys? Me neither that much, but I find them sort of fascinating.  I should run a test comparing them to coded deletes – I bet they would leave delete statements in the dust. Anytime that SQL Server can do something natively, I’m putting my money on the engine that it can process faster whatever you or I might be trying to do in code.

Create the tables to set up our little experimentation:

/****** Object:  Table [dbo].[tbl_1]    Script Date: 11/15/2011 20:57:06 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[tbl_1](
    [Id1] [int] NOT NULL,
    [Value1] [varchar](20) NULL,
 CONSTRAINT [PK_tbl_1] PRIMARY KEY CLUSTERED 
(
    [Id1] 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 SET ANSI_PADDING OFF GO /****** Object: Table [dbo].[tbl_2] Script Date: 11/15/2011 20:57:06 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[tbl_2]( [Id2] [int] NOT NULL, [Id1] [int] NULL, [Value2] [varchar](20) NULL, CONSTRAINT [PK_tbl_2] PRIMARY KEY CLUSTERED ( [Id2] 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 SET ANSI_PADDING OFF GO /****** Object: Table [dbo].[tbl_3] Script Date: 11/15/2011 20:57:06 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[tbl_3]( [Id3] [int] NOT NULL, [Id2] [int] NULL, [Value3] [varchar](20) NULL, CONSTRAINT [PK_tbl_3] PRIMARY KEY CLUSTERED ( [Id3] 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 SET ANSI_PADDING OFF GO /****** Object: Table [dbo].[tbl_4] Script Date: 11/15/2011 20:57:06 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[tbl_4]( [Id4] [int] NOT NULL, [Id3] [int] NULL, [Id2] [int] NULL, [Value4] [varchar](20) NULL, CONSTRAINT [PK_tbl_4] PRIMARY KEY CLUSTERED ( [Id4] 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 SET ANSI_PADDING OFF GO /****** Object: ForeignKey [FK_tbl_2_tbl_1] Script Date: 11/15/2011 20:57:06 ******/ ALTER TABLE [dbo].[tbl_2] WITH CHECK ADD CONSTRAINT [FK_tbl_2_tbl_1] FOREIGN KEY([Id1]) REFERENCES [dbo].[tbl_1] ([Id1]) ON DELETE CASCADE GO ALTER TABLE [dbo].[tbl_2] CHECK CONSTRAINT [FK_tbl_2_tbl_1] GO /****** Object: ForeignKey [FK_tbl_3_tbl_2] Script Date: 11/15/2011 20:57:06 ******/ ALTER TABLE [dbo].[tbl_3] WITH CHECK ADD CONSTRAINT [FK_tbl_3_tbl_2] FOREIGN KEY([Id2]) REFERENCES [dbo].[tbl_2] ([Id2]) ON DELETE CASCADE GO ALTER TABLE [dbo].[tbl_3] CHECK CONSTRAINT [FK_tbl_3_tbl_2] GO /****** Object: ForeignKey [FK_tbl_4_tbl_3] Script Date: 11/15/2011 20:57:06 ******/ ALTER TABLE [dbo].[tbl_4] WITH CHECK ADD CONSTRAINT [FK_tbl_4_tbl_3] FOREIGN KEY([Id3]) REFERENCES [dbo].[tbl_3] ([Id3]) ON DELETE CASCADE GO ALTER TABLE [dbo].[tbl_4] CHECK CONSTRAINT [FK_tbl_4_tbl_3] GO


I’ve created four tables, all with a PK and an FK that include DELETE CASCADE; so what’s happening here is that when I delete a record in tbl_1, the foreign keys through to tbl_4 will delete the associated foreign key records. Rather than write some trigger or delete the associated rows in some other fashion, I’m letting SQL Server do it.

Compile this procedure (it’s complicated I know right?) and then take a look at the execution plan. If you forgot, just highlight the stored procedure name in a management studio query window and type control + L:

DROP PROCEDURE proc_cascadetest
GO
CREATE PROCEDURE proc_cascadetest
AS 
    DELETE tbl_1 WHERE Id1=1
GO 

Notice that the execution plan shows the CASCADE DELETE action for the stored procedure; each step for the foreign key tables is clearly found in the plan. This sort of coincides with what Microsoft said awhile back in the following post on Microsoft Support.

You cannot trace cascading deletes and updates in the context of regular events such asTSQL:StmtCompleted and TSQL:BatchCompleted. However, when you execute a statement that performs cascading updates or deletes in SQL Server Profiler, you can trace these cascade actions in the execution plan.

So, these in fact cannot be traced in SQL Profiler. Then, I remember the hundreds or maybe thousands of blogs by Jonathan Keyhaias on the internet about Extended Events in SQL Server 2008 R2. I think this dude maybe invented them and put them into the SQL Engine or something, I’m not quite sure. Since these are supposed to be the all-powerful, all-knowing for internals or so I’m told or led to believe, I thought I’d give them a shot and see if they could serve up the SQL text since profiler can’t. Let’s give it a go. Run the following:

First get the data into our tables…all that I’m doing here is creating the FK equal to the PK + 1, with the FK inserted alongside. Run the script and then go look at the data:

 
DECLARE @t1_insert int
        ,@t2_insert int
        ,@t3_insert int
        ,@t4_insert int
SET
@t1_insert=1 SET @t2_insert = @t1_insert+1 SET @t3_insert = @t2_insert+1 SET @t4_insert = @t3_insert+1 WHILE (@t1_insert < 5) BEGIN INSERT tbl_1 VALUES (@t1_insert, 'tbl1 val') INSERT tbl_2 VALUES (@t2_insert, @t1_insert, 'tbl2 val') INSERT tbl_3 VALUES (@t3_insert, @t2_insert, 'tbl3 val') INSERT tbl_4 VALUES (@t4_insert,@t3_insert, @t2_insert, 'tbl4 val') SET @t1_insert+=1 SET @t2_insert+=1 SET @t3_insert+=1 SET @t4_insert+=1 END
GO

Run this code now to start up extended events session. Make sure and change the sqlserver.database_id for your particular database ID. And, don’t run the script below all at once – maybe just the first part to create the session, then run the stored procedure, and then the part to kill the session:

-- Create the event session and add some events
DROP EVENT SESSION track_sprocs ON SERVER
GO
CREATE EVENT SESSION track_sprocs ON SERVER
ADD EVENT sqlserver.module_end (ACTION (sqlserver.tsql_stack, sqlserver.sql_text) 
WHERE sqlserver.database_id=6), ADD EVENT sqlserver.sql_statement_starting (ACTION (sqlserver.tsql_stack, sqlserver.sql_text)
WHERE sqlserver.database_id=6), ADD EVENT sqlserver.sql_statement_completed (ACTION (sqlserver.tsql_stack, sqlserver.sql_text)
WHERE sqlserver.database_id=6) ADD TARGET package0.ring_buffer WITH (MAX_DISPATCH_LATENCY = 1 SECONDS) GO -- Start the session ALTER EVENT SESSION track_sprocs ON SERVER STATE = START GO -- Add another event ALTER EVENT SESSION track_sprocs ON SERVER ADD EVENT sqlserver.lock_released -- Run the test procedure EXEC proc_cascadetest GO -- Drop the events now ALTER EVENT SESSION track_sprocs ON SERVER DROP EVENT sqlserver.module_end, DROP EVENT sqlserver.sql_statement_completed, DROP EVENT sqlserver.lock_released, DROP EVENT sqlserver.sql_statement_starting GO
DROP EVENT SESSION track_sprocs ON SERVER GO


Now run either or both of the following: I don’t see the code that shows me that the delete has happened in the CASCADE action that I coded. WTF?

SELECT top 1000 
    event_xml.value('(./@name)', 'varchar(100)') as [event_name],
    event_xml.value('(./data[@name="object_name"]/value)[1]', 'varchar(255)') as [object_name],
    CAST(event_xml.value('(./action[@name="tsql_stack"]/value)[1]','varchar(MAX)') as XML) as 
[stack_xml], event_xml.value('(./action[@name="sql_text"]/value)[1]', 'varchar(max)') as [sql_text] FROM #xml_event_data CROSS APPLY xml_data.nodes('//event') n (event_xml)

image

SELECT TOP 20 SUBSTRING(st.text, (qs.statement_start_offset/2)+1, 
        ((CASE qs.statement_end_offset
          WHEN -1 THEN DATALENGTH(st.text)
         ELSE qs.statement_end_offset
         END - qs.statement_start_offset)/2) + 1) AS statement_text,*
FROM sys.dm_exec_query_stats AS qs
    CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st
ORDER BY Creation_Time DESC

image


Both images above clearly show that the code that does the RI isn’t in the session that I ran.

My conclusion – none. I tried adding several events in my Extended Event session (is this the same as what people are calling “xevents”?) and couldn’t get anything to pop out. Anyone have the phone number to SQLSkills.com?

Thanks for reading,
Lee Everest

 

-------------------------

I laid an egg on this one – you try it and give me a shout if you figure it out. I’m gonna go drink a beer. Bye.


Posted in: SQL Administration , SQL Server 2008/R2 , TSQL  Tags:
blog comments powered by Disqus

by Lee Everest, M.S.

Info

Poll

Do you use Azure or cloud in your organization?



Show Results

Ads

Search


Month List

Calendar

«  May 2012  »
MoTuWeThFrSaSu
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910
View posts in large calendar

Tags

Disclaimer
The opinions, code, examples, et.al. expressed herein are my own personal opinions and do not represent my employer's view in any way, shape form, or fashion.  All code for demonstration purposes - no guarantees, either written or implied, are made.

© Copyright 2012 Lee Everest's SQL Server, etc. weblog