Is it OK to use global temporary table? - sql

I have a query which conceptually can be described like this:
CREATE TABLE ##MyTable (
-- rows
)
INSERT INTO ##MyTable (...)
/*inserted SELECT */
WHILE ....
BEGIN
-- do some actions using data from temp table
END
EXEC msdb.dbo.sp_send_dbmail
-- other data needed for email sending ...
#query = N'select ... FROM ##MyTable;',
-- drop the temporary table
DROP TABLE ##MyTable
So, i select some data to the global temp table, them work with it, them send email and finally delete this temp table.
This query is used as a task, which launches periodically to automate some processes in my DB.
The moment I doubt in - is global temporary table. If i plan to use this table (with such name) only in this automatisation script, can i be sure that there will be no collisions or some other similar bugs? It looks like it should not be, cause no users or program connections are going to use this table, so the logic is simple: my task launches once a week, creates this table then deletes it. But is it really so, or i miss some moments, and it is not a good idea to use global temporary table here?
PS: i've tried to use local temp. tables, but sp_send_dbmail returns an error (as far as i understand a table is deleted already when sp_send_dbmail launches):
Msg 22050, Level 16, State 1, Line 0
Error formatting query, probably invalid parameters
Msg 14661, Level 16, State 1, Procedure sp_send_dbmail, Line 504
Query execution failed: Msg 208, Level 16, State 1, Server SERVER\SERVER, Line 1
Invalid object name '#MyTable'.

You are correct that a session temporary table can't be used with sp_send_dbmail. To quote from the docs, emphasis mine:
[ #query= ] 'query' Is a query to execute. The results of the query
can be attached as a file, or included in the body of the e-mail
message. The query is of type nvarchar(max), and can contain any valid
Transact-SQL statements. Note that the query is executed in a
separate session, so local variables in the script calling
sp_send_dbmail are not available to the query.
Global temporary tables can be created by any user so there is a possibility of a clash here. This can happen if one execution of the task takes too long and overlaps with the next run. There are three ways I might consider to resolve this.
Use the NEWID() function to create the name of the temporary table. This would ensure two executions of the script create tables with different names.
Use a permanent table with a column in it that is uniquely set by each execution of the script so that it can be referred to by the query passed to sp_send_dbmail.
I might consider using sp_getapplock to create a lock that other scripts could check to see if the table was in use. (Note if the script executions routinely overlap this may cause a backlog to build up).

A global temporary table means that any other user can also try to create the same global temporary table. This would cause collision.
In most cases, creating and using a permanent table has served us well. You can lot of advantages with a permanent table. You can have a history of things done. If you think the data will grow, you can setup house keeping to delete data older than some days or weeks.
In our projects our guidance is: either create a "real" temporary table or a "real" permanent table.

Not using global temporary table. Convert your query output to HTML body, here may help you https://dba.stackexchange.com/questions/83776/need-to-send-a-formatted-html-email-via-database-mail-in-sql-server-2008-r2
Using global temporary table but consider to reduce the chance of collision
a. try #Martin Brown's suggestion.
b. if your while loop takes some time to finish, you can create a local temporary table for it first. Only dump the output to global temp table right before database mail. And drop it immediately after mail sent.

Related

SQL Server temp table check performance

IF OBJECT_ID('tempdb..#mytesttable') IS NOT NULL
DROP TABLE #mytesttable
SELECT id, name
FROM
INTO #mytesttable mytable
Question: is it good to check temp table existence (e.g: OBJECT_ID('tempdb..#mytesttable') ) when I first time create this temp table inside a procedure?
What will be the best practice in terms of performance?
It is good practice to check if table exists or not. and it doesn't have any performance impact. anyhow, temp tables automatically drops once procedure execution completes in sql server. sql server appends unique num with temp table name. so if same procedure executes more than once at same time also , it will not cause any issue. it depends on session. all procs executions have different session.
Yes, it is a good practice. I am always doing this at the beginning of the routine. In SQL Server 2016 + objects can DIE (Drop If Exists), so you can simplify the call:
DROP TABLE IF EXISTS #mytesttable;
Also, even at the end of the routine you temporary objects are destroyed and even the SQL Engine gives unique names to temporary tables, there is still another behavior to consider.
If you are naming your temporary table with same name, when nested procedure calls are involved (one stored procedure calls another) it is possible to get an error or corrupt your data. This is due to the fact that temporary table are visible in the current execution scope - so, #table in one procedure will be visible in the second called procedure (so you can modify its data or drop it).

How Temp Table works in Stored Procedures (global & local )

Image #1: I am creating a stored procedure for inserting records into #t1.
In the same session I am executing the Localtemp1 stored procedure for any number of times, and it works fine:
Image #2: again executing the stored procedure in another session & works fine as well:
Image #3: now creating stored procedure for inserting records into ##tt. For the first execution of globaltemp1 stored procedure, it works well:
Image #4: but when I executed it a second time, it is showing errors (does not exist in DB):
Image #5: then I closed the session where globaltemp stored procedure was created, and in a new session, I executed the stored procedure, and it works well for the first time:
Image #6: but when I execute it a second time, again it is showing errors (does not exist in DB):
What I know is scope of local temp & global temp, but in stored procedures, they were completely different
Can someone tell me
Execution of localtemp1 stored procedure for many times gives output but while executing globaltemp1 sp for the first time gives output and second time results in an error
As far as I know, after execution of stored procedure temptable gets dropped. Then why localtemp1 stored procedure is getting executed across all sessions and many number of times?
Why globaltemp1 stored procedure is executing once and for second time showing an error?
Final one, Globaltemp stored procedure shows output in another session for the first time only when created session was closed
I mean
56 ----> globaltemp sp was created
57 ----> to get o/p i need to close 56
58 ----> to get o?p i need to close 57 ( WHY ??? )
I am a beginner at SQL and please, someone make me understand because if I don't find logic & correct reason I could not dive into another topic.
The concept of temp table is to hold records temporarily. It's some kind of an array where you can store multiple records using the same variable.
When you create a Temp Table, actually it is being created in the tempdb of the corresponding server. Even if you are naming it as just #temp, the name on which it was created on the tempdb will be having some additional parameters like your database name from which the table was created and your session id etc.
I just created the following temp table in my master database
and this is how it was named in the tempdb
still, in my database, I can access it using the name #temp. But The limitation of such temp table is that they are local and can be accessed only from that session, So if I try to access this #temp from any other Query Window (Session) even on the same database, I won't be able to access it. That's where we use Global temp tables. So If I add one more # to the table name then it becomes global temp table which can be accessed across the sessions. It is still created on the Tempdb but like this
Whenever you close the query window/session both Local and Global temp tables are automatically dropped.
So in the case of stored procedures, the starting and ending time of the sp is treated as one session. So once the sp execution is completed all the temp tables created inside that sp is dropped. So you can not use one temp table that was created by one SP in another one.
Hope this helps
Local temporary tables are visible only in the current session, and global temporary tables are visible to all sessions. Global temporary tables are automatically dropped when the session that created the table ends and all other tasks have stopped referencing them. The association between a task and a table is maintained only for the life of a single Transact-SQL statement. This means that a global temporary table is dropped at the completion of the last Transact-SQL statement that was actively referencing the table when the creating session ended.
If you have absolutely have to have a global temp table available your best solution would be to create a permanent table and then drop the table at the end of the stored procedure. You can check for it's existence before creating it:
IF OBJECT_ID('dbo.yourtablenamehere', 'U') IS NOT NULL
DROP TABLE dbo.yourtablenamehere;
The differences between a temp table and a permanent table are really not that much different, mostly that the temp table drops automatically. If you are using this in an application that is calling this procedure, it might be best to have the application load the temp table into array and do the comparisons for you since it can maintain the array while executing and re-executing stored procs.
There is a good reason to write to a temp table instead of creating a table then dropping it... access rights. If you are creating tables to be used by people who are only granted read only privileges, they will not be able to create the table.

Sybase ASE temp table not found

I have a temporary table called table1 created like this:
create tabletempdb..table1(id int)
The table was not created by the owner of the tempdb database.
When i tried earlier to access the table with this query, inside a stored procedure ( just for testing ):
select top 10 * from tempdb..table1
I got this error:
Msg 208, Level 16, State 1:
Server 'SERVER', Procedure 'storedProcedure', Line 30:
tempdb..table1 not found. Specify owner.objectname or use sp_help to check whether the object
exists (sp_help may produce lots of output).
However, about an hour later the same stored procedure ran without any problems.
The table was not dropped and created again during that hour and I cannot find any reason for this strange behavior. I could fix this by applying some kind of naming hack, but I do not want to insert hacks into a quite sensitive flow, meaning a lot of users could drop and create the table.
I am asking if someone could explain this behavior so I can avoid it from now on.

Global temporary tables in SQL Server

I have a ##table which can be accessed across all the sessions but sometimes I am getting error
There is already an object named
'##table' in the database.
WHY and how to resolve it.
Found an interesting reference (outdated URL referencing what is now a malicious website removed.):
Global temporary tables operate much like local temporary tables; they are created in tempdb and cause less locking and logging than permanent tables. However, they are visible to all sessions, until the creating session goes out of scope (and the global ##temp table is no longer being referenced by other sessions). If two different sessions try the above code, if the first is still active, the second will receive the following:
Server: Msg 2714, Level 16, State 6, Line 1
There is already an object named '##people' in the database.
I have yet to see a valid justification for the use of a global ##temp table. If the data needs to persist to multiple users, then it makes much more sense, at least to me, to use a permanent table. You can make a global ##temp table slightly more permanent by creating it in an autostart procedure, but I still fail to see how this is advantageous over a permanent table. With a permanent table, you can deny permissions; you cannot deny users from a global ##temp table.
There is already an object named '##table' in the database.
You would typically get this error if you are doing a CREATE Table statement which would obviously fail as '##table' already exists in the database.
Seems to me that maybe at some point in your code, the CREATE TABLE logic for this global table is being invoked again leading to this error.
Do the have the details of the exact statement that results in this error?
So the WHY part has been answered and here is how to resolve it:
Do a check to see if the temp table exists before creating it:
if object_id('tempdb..##table') is null begin
--create table ##table...
end
I found a pretty interesting post about how to check the existence of a temp table from Googling http://sqlservercodebook.blogspot.com/2008/03/check-if-temporary-table-exists.html

View Temporary Table Created from Stored Procedure

I have a stored procedure in SQL 2005. The Stored Procedure is actually creating temporary tables in the beginning of SP and deleting it in the end. I am now debugging the SP in VS 2005. In between the SP i would want to know the contents into the temporary table. Can anybody help in in viewing the contents of the temporary table at run time.
Thanks
Vinod T
There are several kinds of temporary tables, I think you could use the table which is not dropped after SP used it. Just make sure you don't call the same SP twice or you'll get an error trying to create an existing table. Or just drop the temp table after you see it's content. So instead of using a table variable (#table) just use #table or ##table
From http://arplis.com/temporary-tables-in-microsoft-sql-server/:
Local Temporary Tables
Local temporary tables prefix with single number sign (#) as the first character of their names, like (#table_name).
Local temporary tables are visible only in the current session OR you can say that they are visible only to the current connection for the user.
They are deleted when the user disconnects from instances of Microsoft SQL Server.
Global temporary tables
Global temporary tables prefix with double number sign (##) as the first character of their names, like (##table_name).
Global temporary tables are visible to all sessions OR you can say that they are visible to any user after they are created.
They are deleted when all users referencing the table disconnect from Microsoft SQL Server.
Edit the stored procedure to temporarily select * from the temp tables (possibly into another table or file, or just to the output pane) as it runs..?
You can then change it back afterwards. If you can't mess with the original procedure, copy it and edit the copy.
I built a few stored procedures which allow you to query the content of a temp table created in another session.
See sp_select project on github.
The content of the table can be displayed by running exec sp_select 'tempdb..#temp' from no matter which session.
Bottom line: the default Visual Studio Microsoft debugger is not in the same session as the SQL code being executed and debugged.
So you can ONLY look at #temp tables by switching them to global ##temp tables or permanent tables or whatever technique you like best that works across sessions.
note: this is VERY different from normal language debuggers... and I suspect kept
that way by Microsoft on purpose... I've seen third party SQL debugger tools decades ago
that didn't have this problem.
There is no good technical reason why the debugger cannot be in the same session as your SQL code, thus allowing you to examine all produced contructs including #temp tables.
To expand on previous suggestions that you drop the data into a permanent table, you could try the following:
-- Get rid of the table if it already exists
if object_id('TempData') is not null
drop table TempData
select * into TempData from #TempTable
This helped me.
SELECT * FROM #Name
USE [TEMPDB]
GO
SELECT * FROM syscolumns
WHERE id = ( SELECT id FROM sysobjects WHERE [Name] LIKE '#Name%')
this gives the details of all the temp table