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

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

Related

For how long does a temporary table "live" in dynamic SQL code in Msn SSMS?

I have a problem with temporary tables that does not "live" long enough in my code.
My problem looks like this: I want to create a temporary table in one "codevariable" and use it later. An example of my code structure is like below:
declare #RW varchar(MAX)
set #RW = '
select *
into #temptable
from table1'
exec(#RW)
--Alots of other code.
select *
from #temptable
This results in an error message that sais "Invalid object name '#temptable'. And it's very clear that my temporary table does'nt exists anymore. But I've checked that the table creates in the first step. For example the following code works:
declare #RW varchar(MAX)
set #RW = '
select *
into #temptable
from table1
select *
from #temptable'
exec(#RW)
So my GUESS is that the temporary table only lives within it's code variable. Is there a way to create a temporary table that lives longer? Or, do I just needs to accept this for what it is or am I missing something? I have a work around solution that is not very efficient. What I'm thinking of is creating a regular table which I later delete. This would mean a lot of writing to disks but it's something that the system I work with would survive, but not be happy with. Is there any other way to handle this?
A temporary table only persists for the duration of the scope that declared it. For a "normal" connection that will be when the connection is dropped. For example, if you're using SSMS and open a query window and run CREATE TABLE #T (ID int); it'll create the table. As you're still connected, the table won't be dropped and will still exist. If you run the statement again (without dropping it) you'll get an error that it already exists. As soon as you close that query window, the temporary table will be dropped.
For a dynamic statement, the scope is the duration of that dynamic statement. This means that as soon as the dynamic statement completes, the table will be dropped:
EXEC sys.sp_executesql N'CREATE TABLE #T (ID int);';
SELECT *
FROM #t;
Notice this errors, as the scope the table was created in has completed, and thus dropped.
If you are using dynamic statements to create temporary tables, you need to make all the references to said temporary table within the dynamic statement.
Otherwise, if you need to reference it outside of the statement, I personally find I create an "permanent" object in tempdb, and then clean up afterwards.
EXEC sys.sp_executesql N'CREATE TABLE tempdb.dbo.T (ID int);';
SELECT *
FROM tempdb.dbo.T;
DROP TABLE tempdb.dbo.T;
These tables are still dropped in the event the instance is restarted as well.
Note that "global" temporary table behave slightly differently. As global temporary table can be referenced in any connection, while it exists. This means that another connection could be using the table while the scope that created it ends. As a result a global temporary table persists until the scope that declared is ends and there are no other active connections using the object. This means that the objects could be dropped mid batch in another connection.

The "proper" way to atomically replace all contents in a PostgreSQL table?

In the project I have been recently working on, many (PostgreSQL) database tables are just used as big lookup arrays. We have several background worker services, which periodically pull the latest data from a server, then replace all contents of a table with the latest data. The replacing has to be atomic because we don't want a partially completed table to be seen by lookup-ers.
I thought the simplest way to do the replacing is something like this:
BEGIN;
DELETE FROM some_table;
COPY some_table FROM 'source file';
COMMIT;
But I found a lot of production code use this method instead:
BEGIN;
CREATE TABLE some_table_tmp (LIKE some_table);
COPY some_table_tmp FROM 'source file';
DROP TABLE some_table;
ALTER TABLE some_table_tmp RENAME TO some_table;
COMMIT;
(I omit some logic such as change the owner of a sequence, etc.)
I just can't see any advantage of this method. Especially after some discoveries and experiments. SQL statements like ALTER TABLE and DROP TABLE acquire an ACCESS EXCLUSIVE lock, which even blocks a SELECT.
Can anyone explain what problem the latter SQL pattern is trying to solve? Or it's wrong and we should avoid using it?

SQL use temporary tables in trigger

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.

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.

PL/SQL embedded insert into table that may not exist

I much prefer using this 'embedded' style inserts in a pl/sql block (opposed to the execute immediate style dynamic sql - where you have to delimit quotes etc).
-- a contrived example
PROCEDURE CreateReport( customer IN VARCHAR2, reportdate IN DATE )
BEGIN
-- drop table, create table with explicit column list
CreateReportTableForCustomer;
INSERT INTO TEMP_TABLE
VALUES ( customer, reportdate );
END;
/
The problem here is that oracle checks if 'temp_table' exists and that it has the correct number of colunms and throws a compile error if it doesn't exist.
So I was wondering if theres any way round that?! Essentially I want to use a placeholder for the table name to trick oracle into not checking if the table exists.
EDIT:
I should have mentioned that a user is able to execute any 'report' (as above). A mechanism that will execute an arbitrary query but always write to the temp_table ( in the user's schema). Thus each time the report proc is run it drops the temp_table and recreates it with, most probably, a different column list.
You could use a dynamic SQL statement to insert into the maybe-existent temp_table, and then catch and handle the exception that occurs when the table doesn't exist.
Example:
execute immediate 'INSERT INTO '||TEMP_TABLE_NAME||' VALUES ( :customer, :reportdate )' using customer, reportdate;
Note that having the table name vary in a dynamic SQL statement is not very good, so if you ensure the table names stay the same, that would be best.
Maybe you should be using a global temporary table (GTT). These are permanent table structures that hold temporary data for an Oracle session. Many different sessions can insert data into the same GTT, and each will only be able to see their own data. The data is automatically deleted either on COMMIT or when the session ends, according to the GTT's definition.
You create the GTT (once only) like this:
create globabal temporary table my_gtt
(customer number, report_date date)
on commit delete/preserve* rows;
* delete as applicable
Then your programs can just use it like any other table - the only difference being it always begins empty for your session.
Using GTTs are much preferable to dropping/recreating tables on the fly - if your application needs a different structure for each report, I strongly suggest you work out all the different structures that each report needs, and create separate GTTs as needed by each, instead of creating ordinary tables at runtime.
That said, if this is just not feasible (and I've seen good examples when it's not, e.g. in a system that supports a wide range of ad-hoc requests from users), you'll have to go with the EXECUTE IMMEDIATE approach.