SQL use temporary tables in trigger - sql

I have a trigger in MSSQL Server 2008R2 :
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[trg_HosFile_Delete]
ON [dbo].[hosfile] FOR DELETE
AS
insert into #pys(pyGuid)
SELECT EntryGuid AS pyGuid FROM er000 AS er
insert into t2(C1) select pyGuid from #pys
After the trigger has executed the t2 table is empty. Why is it empty?
If I execute the query above without a trigger the t2 table is filled.
is there any problem with using a temporary table in a trigger?

Damien's answer is correct: you can use temporary tables in triggers, but it is strongly recommended to define them there, since the trigger can fire in various contexts.
If you use temporary tables in triggers, check for temporary table existence as you do not control the context and it might be already there:
IF OBJECT_ID('tempdb..#pys') IS NOT NULL
DROP TABLE #pys
Also, temporary tables can be created on the fly:
SELECT * INTO #tmp
FROM inserted
This is particularly useful when trigger contains dynamic SQL that needs to access inserted or deleted special tables which are not visible in the dynamic SQL scope.
Avoid using ##tmp (global temporary variables), as these are globally visible and can lead to trouble when multiple SPIDs are firing you trigger.

There's no problem using temp tables, provided that they're in scope at the time the trigger fires.
Given that the trigger can fire at any time, on any connection, the only scope that makes sense is within the body of the trigger:
ALTER TRIGGER [dbo].[trg_HosFile_Delete]
ON [dbo].[hosfile] FOR DELETE
AS
CREATE TABLE #pys (pyGuid uniqueidentifier not null/*I'm guessing*/)
insert into #pys(pyGuid)
SELECT EntryGuid AS pyGuid FROM er000 AS er
insert into t2(C1) select pyGuid from #pys
(To be honest, I'm not sure if you could access a temp table from an outer scope, and don't have an instance handy to test it. But, even if you can, it would make for a very brittle trigger)

If you use ##pys (notice the double # sign) it will be globally available after its creation. That might help in your case.

Try to use SELECT INTO to create and populate the temporary table. There are some restrictions when using a temp table inside a trigger.

Related

Why doesn't a PL/SQL block create a temporary table?

I would like to create and populate temporary table with data to process it inside loop statement like this:
DECLARE
cnt NUMBER;
BEGIN
SELECT COUNT(tname) INTO cnt from tab where tname = 'MY_TEMP';
IF (cnt > 0) THEN
EXECUTE IMMEDIATE 'DROP TABLE MY_TEMP';
END IF;
EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE MY_TEMP (G NVARCHAR2(128), F NVARCHAR2(128), V NVARCHAR2(128)) ON COMMIT DELETE ROWS';
INSERT INTO MY_TEMP VALUES (N'G-value1', N'F-value1', N'V-value1');
INSERT INTO MY_TEMP VALUES (N'G-value2', N'F-value2', N'V-value2');
...
FOR record IN (SELECT G,F,V FROM MY_TEMP)
LOOP
... Do something sophisticated with record.G, record.F, record.V
END LOOP;
COMMIT;
END;
When I run this script inside PL-SQL Developer it tells me for the very first INSERT that MY_TEMP table or view doesn't exist even though my EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE ... ' statement seems to be executed without errors. I checked there is no MY_TEMP table inside tables list after script execution
When I run EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE ... ' alone it runs ok and MY_TEMP table is really created. After this the whole scripts runs ok.
How do I use this script without manually precreating MY_TEMP table ?
How do I use this script without manually precreating MY_TEMP table ?
You can't. Unless of course you run everything after the creation of the temporary table using EXECUTE IMMEDIATE. But I cannot for a second recommend that approach.
The point is not that your script fails to run, but that it fails to compile. Oracle won't start running your block if it can't compile it first. At the point Oracle tries to compile your PL/SQL block, the table doesn't exist. You have a compilation error, not a runtime error.
I suspect that you are more familiar with temporary tables in SQL Server and are trying to use temporary tables in Oracle in the same way. If this is the case, then you will need to know that there are differences between temporary tables in Oracle and in SQL Server.
Firstly, there's no such thing as a local temporary table (i.e. a table visible to only one connected user) in Oracle. Oracle does have global temporary tables, but only the data in a global temporary table is temporary. The table itself is permanent, so once it has been created it will only be dropped if you explicitly drop it. Compare this with SQL Server temporary tables, which are dropped once all relevant users disconnect.
I really don't think you need to be creating the temporary table in your block. It should be sufficient to create it once beforehand.
Why do want to drop and create the temp table? Simply create it and use it.
The only way around for your problem is to make the whole INSERT INTO temp_table statements into EXECUTE IMMEDIATE in this way you can BYPASS the TABLE check during COMPILE Time first.
But this way in my opinion is not good at all. There are some questions in my mind which has be answred before answering this question.
1) Why Temp Table is created evertime and Dropped.
We have option in GTT to keep or Remove Data after one Oracle Session.
2) Is this script a one time job ? If Yes then we can go for once GTT creation and the rest script will work fine.
The probloem is not wityh your first insert. It is with the compile of your block. The table does not exist, but you are referencing it. Try creating it beforhand so it is there such as it will be once the bloick finishes. Now the code is likely to compile as the reference to the table exists when you run it.
However, then you'll get into trouble with the drop as your code has a share lock on the table so you are not allowed to drop it.
You either have to make your selects dynamic, or make sure the table is created and dropped outrside the execution of your block.
Creating temporary table in Oracle is not best practice, instead use PIVOT

Stored procedure temporary table causing timeout error

I have a temporary table in the stored procedure which is causing the time out for the query as it is doing a complex calculation. I want to drop it after it is used. It was created like
DECLARE #SecondTable TABLE
Now I cannot drop it using
drop #SecondTable
in fact I have to use
drop #SecondTable
Does somebody know why?
I'm by no means a SQL guru, but why is the drop even necessary?
If it's a table variable, it will no longer exist once the stored proc exits.
I'm actually surprised that DROP #SecondTable doesn't error out on you; since you're dropping a temporary table there; not a table variable.
EDIT
So based on your comment, my updates are below:
1.) If you're using a table variable (#SecondTable); then no drop is necessary. SQL Server will take care of this for you.
2.) It sounds like your timeout is caused by the calculations using the table, not the dropping of the table itself. In this case; I'd probably recommend using a temporary table instead of a table variable; since a temporary table will let you add indexes and such to improve performance; while a table variable will not. If this still isn't sufficient; you might need to increase the timeout duration on the query.
3.) In SQL; a table variable (#SecondTable) and temporary table (#SecondTable) are two completely different things. I'd refer to the MSDN documentation for Table Variables and Temporary Tables

Can dynamic SQL be called from a trigger in Oracle?

I have a dozen tables of whom I want to keep the history of the changes. For every one I created a second table with the ending _HISTO and added fields modtime, action, user.
At the moment before I insert, modify or delete a record in this tables I call ( from my delphi app ) a oracle procedure that copies the actual values to the histo table and then do the operation.
My procedure generates a dynamic sql via DBA_TAB_COLUMNS and then executes the generated ( insert into tablename_histo ( fields s ) select fields, sysdate, 'acition', userid from table_name
I was told that I can not call this procedure from a trigger because it has to select the table the trigger is triggered on. Is this true ? Is it possible to implement what I need ?
Assuming you want to maintain history using triggers (rather than any of the other methods of tracking history data in Oracle-- Workspace Manager, Total Recall, Streams, Fine_Grained Auditing etc.), you can use dynamic SQL in the trigger. But the dynamic SQL is subject to the same rules that static SQL is subject to. And even static SQL in a row-level trigger cannot in general query the table that the trigger is defined on without generating a mutating table exception.
Rather than calling dynamic SQL from your trigger, however, you can potentially write some dynamic SQL that generates the trigger in the first place using the same data dictionary tables. The triggers themselves would statically refer to :new.column_name and :old.column_name. Of course, you would have to either edit the trigger or re-run the procedure that dynamically creates the trigger when a new column gets added. Since you, presumably, need to add the column to both the main table and the history table, however, this generally isn't too big of a deal.
Oracle does not allow a trigger to execute a SELECT against the table on which the trigger is defined. If you try it you'll get the dreaded "mutating table" error (ORA-04091), and while there are ways to get around that error they add a lot of complexity for little value. If you really want to build a dynamic query every time your table is updated (IMO this is a bad idea from the standpoint of performance - I find that metadata queries are often slow, but YMMV) it should end up looking something like
strAction := CASE
WHEN INSERTING THEN 'INSERT'
WHEN UPDATING THEN 'UPDATE'
WHEN DELETING THEN 'DELETE'
END;
INSERT INTO TABLENAME_HISTO
(ACTIVITY_DATE, ACTION, MTC_USER,
old_field1, new_field1, old_field2, new_field2)
VALUES
(SYSDATE, strAction, USERID,
:OLD.field1, :NEW.field1, :OLD.field2, :NEW.field2)
Share and enjoy.

Temporary Table Scope?

I am making use of temporary tables #tempTable in my stored procedure - that I make use to run my ASP.net Reports (Reporting services)
I am doing something like
eg. Code
SELECT * INTO #tempTable FROM Contacts WHERE ContactID < 10
Then I use something like
SELECT o.* FROM #tempTable t INNER JOIN Orders o ON t.ContactID =o.ContactID
to return values to my reports aka results for the stored procedure
I do not get rid of my #tempTable
i.e. I don't do
DROP TABLE #tempTable
I have read that the scope of temporary table is only for the stored procedure - so is doing the above necessary - if I dont do the above what problems will I get into in the future
First, local temporary tables created within a procedure are dropped once the procedure finishes. From the BOL on Create Table:
A local temporary table created in a stored procedure is dropped automatically when the stored procedure is finished. The table can be referenced by any nested stored procedures executed by the stored procedure that created the table. The table cannot be referenced by the process that called the stored procedure that created the table.
If your data access code is properly opening a connection, calling a stored procedure and then closing the connection, the temp table is created in the procedure is effectively destroyed.
I say "effectively" to bring up another point. I would not recommend dropping the temp table at the end of your procedure although I would add a check just before I created the temp table and drop it if exists (e.g. if object_id('tempdb..#Foo') is not null). The argument against dropping the temp table at the end is that by calling the Drop statement, you are forcing SQL Server to expend resources to destroy the table then and there while you wait for your procedure to end. If instead, you let it go out of scope, your procedure ends immediately and you let SQL Server destroy the table at a time of its own choosing.
The #Temp table is limited scope to YOUR SESSION and lifespan of the batch, meaning nobody else can see your temp table and anyone else can create their own #Temp table with the same name. Once your session or batch ends, SQL Server will clean up the temp table.
On another note the ##Temp table behaves like a normal table. Everyone can see it, and there cannot be more than 1 ##Temp table with the same name. SQL Server will clean these ##Temp tables when the server restarts.
It's considered good coding practice to explicitly drop every temporary table you create. If you are executing scripts through SQL Server Management Studio/Query Analyzer the temp tables are kept until you explicitly drop them or until you close the session.
In general, you're probably not going to have problems by not dropping temporary tables. The local temp table has session scope, or SP scope in your case. It will drop automatically when the session is closed or the SP completes.
However, you do increase the risk of problems by regularly following this practice. For example, if you don't use an SP, but submit your SELECT statement from ASP .net and leave the SQL Server connection open, the temp table will continue to exist. Continued use of the connection and other temp tables will result in tempdb growth over time.
I also support the other comments regarding the use of temp tables in this case. If you create a solution without temp tables, you'll probably have a faster report and avoid the DROP temp table command too.

DDL statements against deleted inserted in DML trigger

I am trying to find impact of doing DDL statement against deleted and inserted logical tables inside table trigger. I have:
CREATE TRIGGER [Trigger52]
ON [dbo].[Table1]
FOR DELETE, INSERT, UPDATE
AS
BEGIN
create table inserted (c1 int)
select * from inserted
END
When it is triggered, I expected to get an error. Instead, it seems to ignore create table statement entirely and select rows that have been inserted.
Is there a documentation describing this behavior or explanation?
Inside triggers there are always two pseudo-tables existing: inserted and deleted. See Using the inserted and deleted Tables:
SQL Server automatically creates and
manages these tables. You can use
these temporary, memory-resident
tables to test the effects of certain
data modifications and to set
conditions for DML trigger actions.
You cannot directly modify the data in
the tables or perform data definition
language (DDL) operations on the
tables.