Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
Author |
Topic |
Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)
7020 Posts |
Posted - 2010-08-03 : 14:41:39
|
Not really a question here, I just wanted to share something I noticed, and ask if anyone else has run into this.I noticed the following behavior when testing inserts into temp (#) tables vs. declared tables. It appears that inserting into a declared table does not cause a transaction, unless you are inserting from an execute, while inserting into a temp(#) table generates a transaction.It looks like if you want to prevent blocking from the generated transaction, you should insert into a declared table, instead of a temp(#) table.The following script and sample output demonstrates various scenarios, and produces the same results in SQL 2005, 2008, and 2008 R2.use tempdbgoset nocount ongoif object_id('T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B','U') is not null begin drop table T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B endgocreate table T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B(id int not null primary key clustered)go-- Load test datainsert into T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652Bselect id = 2049034185goif object_id('tempdb..#t1','U') is not null begin drop table #t1 endif object_id('tempdb..#t2','U') is not null begin drop table #t2 endif object_id('tempdb..#t3','U') is not null begin drop table #t3 endif object_id('tempdb..#t4','U') is not null begin drop table #t4 endgocreate table #t1 (id int not null primary key clustered, trancount int not null)create table #t2 (id int not null primary key clustered, trancount int not null)create table #t3 (id int not null primary key clustered, trancount int not null)create table #t4 (id int not null primary key clustered,trancount_from_declared_table int not null,trancount int not null)godeclare @d1 table (id int not null primary key clustered, trancount int not null)declare @d2 table (id int not null primary key clustered, trancount int not null)insert into #t1select *, @@trancount from T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652Bprint 'select * from #t1 - Simple insert into temp table'select * from #t1insert into @d1select *, @@trancount from T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652Bprint 'select * from @d1 - Simple insert into declared table'select * from @d1insert into #t2execute ('select *, @@trancount from T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B')print 'select * from #t2 - Insert into temp table from execute statement'select * from #t2insert into @d2execute ('select *, @@trancount from T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B')print 'select * from @d2 - Insert into declared table from execute statement'select * from @d2execute ('insert into #t3select *, @@trancount from T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B')print 'select * from #t3 - Insert into temp table inside execute statement'select * from #t3execute ('declare @d4 table (id int not null primary key clustered, trancount int not null)insert into @d4select *, @@trancount from T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652Bprint ''select * from @d4 - Insert into declared table inside execute statement''select * from @d4insert into #t4select *, @@trancount from @d4print ''select * from #t4 - Insert into temp table from declared table inside execute statement''select * from #t4')goif object_id('T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B','U') is not null begin drop table T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B end/*-- Sample Resultsselect * from #t1 - Simple insert into temp tableid trancount ----------- ----------- 2049034185 2 select * from @d1 - Simple insert into declared tableid trancount ----------- ----------- 2049034185 0 select * from #t2 - Insert into temp table from execute statementid trancount ----------- ----------- 2049034185 1 select * from @d2 - Insert into declared table from execute statementid trancount ----------- ----------- 2049034185 1 select * from #t3 - Insert into temp table inside execute statementid trancount ----------- ----------- 2049034185 2 select * from @d4 - Insert into declared table inside execute statementid trancount ----------- ----------- 2049034185 0 select * from #t4 - Insert into temp table from declared table inside execute statementid trancount_from_declared_table trancount ----------- ----------------------------- ----------- 2049034185 0 2 */ CODO ERGO SUM |
|
mfemenel
Professor Frink
1421 Posts |
Posted - 2010-08-03 : 14:46:26
|
declare tables don't participate in transactions. See example.DECLARE @MsgInfo as table(Msg varchar(1000))Create Table #MsgInfo(Msg varchar(1000))Begin TransactionBEGIN TRY Insert into @MsgInfo(Msg) values('About to update firstnames of A in table var') Insert into #MsgInfo(Msg) values('About to update firstnames of A in temp table') UPDATE CONTACT SET FirstName='B.' WHERE FirstName='A.' RAISERROR('ERROR RAISED',16,1) END TRY BEGIN CATCH ROLLBACK Transaction SELECT * FROM @MsgInfo SELECT * FROM #MsgInfo END CATCHDrop table #MsgInfoMike"oh, that monkey is going to pay" |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2010-08-03 : 14:48:34
|
Yes, table variables does not utilize transactions. Also they have no statistics. It means the query optimizer ALWAYS assumes there is one and one row only in the table variable, no matter how many there are in reality.Also worth mentioning, table variables cannot participate in parallell queries, with the exception of SELECT. INSERT, DELETE and UPDATE cannot be parallellized against a table variable. N 56°04'39.26"E 12°55'05.63" |
 |
|
robvolk
Most Valuable Yak
15732 Posts |
Posted - 2010-08-03 : 14:51:13
|
Yes, it's a nice workaround when you want to log data in a table inside a transaction that could be rolled back. Use a table variable inside the transaction, then insert from that variable into a regular table outside it. |
 |
|
Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)
7020 Posts |
Posted - 2010-08-03 : 14:55:54
|
quote: Originally posted by mfemenel declare tables don't participate in transactions. See example.DECLARE @MsgInfo as table(Msg varchar(1000))Create Table #MsgInfo(Msg varchar(1000))Begin TransactionBEGIN TRY Insert into @MsgInfo(Msg) values('About to update firstnames of A in table var') Insert into #MsgInfo(Msg) values('About to update firstnames of A in temp table') UPDATE CONTACT SET FirstName='B.' WHERE FirstName='A.' RAISERROR('ERROR RAISED',16,1) END TRY BEGIN CATCH ROLLBACK Transaction SELECT * FROM @MsgInfo SELECT * FROM #MsgInfo END CATCHDrop table #MsgInfoMike"oh, that monkey is going to pay"
They may not participate in transactions themselves, but doing an insert into a declared table from an EXECUTE statement generates a transaction that that can produce blocking on the other tables in the transaction. The example in my original post for declared table @d2 shows that the value of @@transcount was 1.Although it is more trouble to code, the final example (@d4 and #t4) of inserting data into the declared table inside the EXECUTE and then inserting the data from the declared data to the temp(#) table eliminates the blocking transaction.CODO ERGO SUM |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2010-08-03 : 15:19:24
|
I think it's the EXECUTE itself that triggers a builtin transaction. The EXECUTE doesn't know beforehand what is going to be executed. N 56°04'39.26"E 12°55'05.63" |
 |
|
Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)
7020 Posts |
Posted - 2010-08-03 : 15:51:17
|
quote: Originally posted by Peso I think it's the EXECUTE itself that triggers a builtin transaction. The EXECUTE doesn't know beforehand what is going to be executed. N 56°04'39.26"E 12°55'05.63"
I don't think the EXECUTE by itself generates the transaction.Notice the results from the INSERT into declared table @d4 inside the EXECUTE shows a value for @@trancount of 0, but the INSERT/EXCUTE for table @d2 shows a value for @@trancount of 1.CODO ERGO SUM |
 |
|
Kristen
Test
22859 Posts |
Posted - 2010-08-04 : 02:26:17
|
"It appears that inserting into a declared table does not cause a transaction"T'is documented in BoL (this from SQL 2000)"because table variables have limited scope and are not part of the persistent database, they are not impacted by transaction rollbacks.""unless you are inserting from an execute"Interesting, didn't know you could do that (certainly couldn't in SQL 2000)I wonder if I could, now in SQL2008, solve a problem that I couldn't find an easy way to do in SQL2000.My Sprocs log that they have started and update that [log row] when they end.A Rollback in an Sproc higher up the tree causes all child sproc logging to be rolled back - I just get the "error" from the higher Sproc in the log. Often would be handy to see what the child sprocs' would have logged.I could copy the log data into a @TableVariable before the Rollback, and reinsert it (changing a column to indicate that the log entries were part of a rollback).But [at the time, back in SQL 2000] I would have to have that code in all my Sprocs. I wanted to call an SProc that would preserve the log data via @TableVariable, do the rollback, and then re-insert the log data from the @TableVariable.Here's the question I asked at the time. Didn't get much traction then - perhaps I built a rather complex example? http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=61277#212435 |
 |
|
Michael Valentine Jones
Yak DBA Kernel (pronounced Colonel)
7020 Posts |
Posted - 2010-08-04 : 15:38:01
|
quote: Originally posted by Kristen "It appears that inserting into a declared table does not cause a transaction"T'is documented in BoL (this from SQL 2000)"because table variables have limited scope and are not part of the persistent database, they are not impacted by transaction rollbacks.""unless you are inserting from an execute"Interesting, didn't know you could do that (certainly couldn't in SQL 2000)I wonder if I could, now in SQL2008, solve a problem that I couldn't find an easy way to do in SQL2000.My Sprocs log that they have started and update that [log row] when they end.A Rollback in an Sproc higher up the tree causes all child sproc logging to be rolled back - I just get the "error" from the higher Sproc in the log. Often would be handy to see what the child sprocs' would have logged.I could copy the log data into a @TableVariable before the Rollback, and reinsert it (changing a column to indicate that the log entries were part of a rollback).But [at the time, back in SQL 2000] I would have to have that code in all my Sprocs. I wanted to call an SProc that would preserve the log data via @TableVariable, do the rollback, and then re-insert the log data from the @TableVariable.Here's the question I asked at the time. Didn't get much traction then - perhaps I built a rather complex example? http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=61277#212435
One thing I have thought about, but never tried is using a CLR stored procedure to do error logging into a database table.I believe that CLR procedures can establish their own connection to the database outside the context of the transaction in the existing connection. If that is true, then you would be able to pass the error messages to a CLR proc to be logged. If you needed to return a result set, I suppose you could pass that to the CLR proc as XML.Has anyone ever tried something like this this?CODO ERGO SUM |
 |
|
mfemenel
Professor Frink
1421 Posts |
Posted - 2010-08-04 : 15:43:15
|
Why Rob, what wonderful presentation did you hear that from? ! ;~Mike"oh, that monkey is going to pay" |
 |
|
robvolk
Most Valuable Yak
15732 Posts |
Posted - 2010-08-04 : 17:01:05
|
quote: Originally posted by mfemenel Why Rob, what wonderful presentation did you hear that from? ! ;~
I'm not telling. |
 |
|
|
|
|
|
|