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.

 All Forums
 SQL Server 2005 Forums
 Transact-SQL (2005)
 Transactions - Temp vs. Declared Tables

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 tempdb
go
set nocount on
go

if object_id('T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B','U') is not null
begin drop table T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B end
go
create table T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B
(id int not null primary key clustered)
go
-- Load test data
insert into T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B
select id = 2049034185

go
if object_id('tempdb..#t1','U') is not null
begin drop table #t1 end
if object_id('tempdb..#t2','U') is not null
begin drop table #t2 end
if object_id('tempdb..#t3','U') is not null
begin drop table #t3 end
if object_id('tempdb..#t4','U') is not null
begin drop table #t4 end
go
create 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
)
go

declare @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 #t1
select *, @@trancount from T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B

print 'select * from #t1 - Simple insert into temp table'
select * from #t1



insert into @d1
select *, @@trancount from T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B

print 'select * from @d1 - Simple insert into declared table'
select * from @d1


insert into #t2
execute ('
select *, @@trancount from T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B
')


print 'select * from #t2 - Insert into temp table from execute statement'
select * from #t2


insert into @d2
execute ('
select *, @@trancount from T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B
')

print 'select * from @d2 - Insert into declared table from execute statement'
select * from @d2


execute ('
insert into #t3
select *, @@trancount from T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B
')


print 'select * from #t3 - Insert into temp table inside execute statement'
select * from #t3


execute ('

declare @d4 table (id int not null primary key clustered, trancount int not null)


insert into @d4
select *, @@trancount from T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B


print ''select * from @d4 - Insert into declared table inside execute statement''

select * from @d4

insert into #t4
select *, @@trancount from @d4


print ''select * from #t4 - Insert into temp table from declared table inside execute statement''
select * from #t4

')


go
if object_id('T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B','U') is not null
begin drop table T_TEMP_1DA72A14FFBC4E0BACB6B6950BC6652B end


/*

-- Sample Results

select * from #t1 - Simple insert into temp table
id trancount
----------- -----------
2049034185 2

select * from @d1 - Simple insert into declared table
id trancount
----------- -----------
2049034185 0

select * from #t2 - Insert into temp table from execute statement
id trancount
----------- -----------
2049034185 1

select * from @d2 - Insert into declared table from execute statement
id trancount
----------- -----------
2049034185 1

select * from #t3 - Insert into temp table inside execute statement
id trancount
----------- -----------
2049034185 2

select * from @d4 - Insert into declared table inside execute statement
id trancount
----------- -----------
2049034185 0

select * from #t4 - Insert into temp table from declared table inside execute statement
id 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 Transaction
BEGIN 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 CATCH
Drop table #MsgInfo

Mike
"oh, that monkey is going to pay"
Go to Top of Page

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"
Go to Top of Page

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.
Go to Top of Page

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 Transaction
BEGIN 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 CATCH
Drop table #MsgInfo

Mike
"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
Go to Top of Page

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"
Go to Top of Page

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
Go to Top of Page

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
Go to Top of Page

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
Go to Top of Page

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"
Go to Top of Page

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.
Go to Top of Page
   

- Advertisement -