Create a global temporary table in Oracle SQL - sql

I am new to SQL. I want to create a (global or not) temporary table in Oracle SQL which will include a simple selection of data of the form SELECT * FROM tbl_NAME WHERE... and which after the end of my session will be deleted (just like the MSFT SQL temporary tables of the form ##tbl_NAME).
I found online that one way to do it is:
CREATE GLOBAL TEMPORARY TABLE tmp_table
SELECT * FROM tbl_NAME WHERE conditions.
ON COMMIT PRESERVE ROWS;
although I get the error ORA-00904: invalid identifier
I also found that another alternative is
CREATE PRIVATE TEMPORARY TABLE tmp_table AS
SELECT * FROM tbl_NAME WHERE conditions;
which gives the error ORA-00905: missing keyword.
Please note that I already know that one alternative that works is:
DROP TABLE tmp_table;
CREATE TABLE tmp_table AS
SELECT * FROM tbl_NAME;
DROP TABLE tmp_table;

What you want to do with rows comes first; SELECT comes next:
SQL> create global temporary table gtt_dept
2 on commit preserve rows --> first
3 as
4 select * from dept; --> next
Table created.
SQL>

The below format you had shown works in TERADATA
CREATE GLOBAL TEMPORARY TABLE tmp_table
SELECT * FROM tbl_NAME WHERE conditions.
ON COMMIT PRESERVE ROWS;
In Oracle it works like this
create global temporary table gtt_dept
on commit preserve rows
as
select * from dept;

Related

How to create tables separately using PostgreSQL?

Suppose I have these simple sql statements
CREATE TABLE a AS (SELECT 1); -- query #1
CREATE TABLE b AS (SELECT 2); -- query #2
The two tables are created only when the two queries are both finished.
If query #2 runs into any error (or takes much longer than the query #1), neither table a nor b will be created (or table a will not be present until query #2 finishes).
I hope there is a way to create tables one by one, that is, after table a is created, then query #2 is allowed to run, table a thus will be saved (or present) even query #2 runs into errors (or takes much longer time to run).
I googled it with several keywords but in vain. Any solution?
If your connection has implicit transactions turned on, all statements must succeed, or they will all be rolled back. You probably know that's how it works when manipulating data (insert, update etc.) but you may not realize it also happens for table creation and many other DDL commands.
You can avoid this behavior by:
Adding an explicit COMMIT after each CREATE TABLE command
Turning on AUTOCOMMIT in your session
Many DBMSs only apply transactions to data manipulation, but some like Postgres do not.
I assume you are talking about transaction?
successful:
t=# begin;
BEGIN
t=# create table so56(i int);
CREATE TABLE
t=# create table so57 as select * from pg_tables limit 1;
SELECT 1
t=# end;
COMMIT
t=# select * from so56;
i
---
(0 rows)
not successful
t=# begin;
BEGIN
t=# create table so56(i int);
CREATE TABLE
t=# select * from so56;
i
---
(0 rows)
t=# create table so57 as select * from not_existing_table_to_raise_error;
ERROR: relation "not_existing_table_to_raise_error" does not exist
LINE 1: create table so57 as select * from not_existing_table_to_rai...
^
t=# end;
ROLLBACK
t=# select * from so56;
ERROR: relation "so56" does not exist
LINE 1: select * from so56;
second case first table creation rollback if first table creation fails

Why I can't create an index on temporary table?

First I created a temporary table
CREATE GLOBAL TEMPORARY TABLE TMP ON COMMIT PRESERVE ROWS AS
SELECT *
FROM A
then
INSERT INTO TMP
SELECT *
FROM B
COMMIT
finally
CREATE INDEX IDX ON TMP (COLA, COLB, COLC);
Upon creating the index, I got the following error
ORA-14452: attempt to create, alter or drop an index on temporary table already in use
I followd these steps in the same session.
What I want to do(with temporary table) is
Disable/Drop index
Insert large data
Enable/Create index
How can I acheive this?
ORA-14452: attempt to create, alter or drop an index on temporary table already in use
This error occurs when it is tried to perform a DDL on a global temporary table with on commit preserve rows when the session trying to do the DDL has already made a DML on the table.
In order to do a DDL, the table must first be either truncated or the session must be exited.
http://www.adp-gmbh.ch/ora/err/ora_14452.html
I have to admit to being surprised by this. The only solution I have come up with so far is to re-establish the session
SQL> drop table tmp;
Table dropped.
SQL>
SQL> CREATE GLOBAL TEMPORARY TABLE TMP ON COMMIT PRESERVE ROWS AS
2 SELECT *
3 FROM emp
4 ;
Table created.
SQL>
SQL>
SQL> INSERT INTO TMP
2 SELECT *
3 FROM emp
4 ;
14 rows created.
SQL>
SQL> COMMIT
2 ;
Commit complete.
SQL>
SQL> connect scott/tiger
Connected.
SQL>
SQL> CREATE INDEX IDX ON TMP (empno);
Index created.
When you insert lines into a temp table it gets locked and the index can't be created.
You should create the index before inserting the data:
CREATE GLOBAL TEMPORARY TABLE tmp ON COMMIT PRESERVE ROWS AS
SELECT *
FROM scott.dept;
CREATE INDEX idx ON
tmp (deptno);
ora-14452: tentativa de criar, alterar ou eliminar um índice em uma tabela temporária que já está sendo usada
14452. 00000 - "attempt to create, alter or drop an index on temporary table already in use"
*Cause: An attempt was made to create, alter or drop an index on temporary
table which is already in use.
*Action: All the sessions using the session-specific temporary table have
to truncate table and all the transactions using transaction
specific temporary table have to end their transactions.
TRUNCATE TABLE tmp;
Table TMP truncado.
CREATE INDEX idx ON tmp (deptno);
Index idx criado.
INSERT INTO tmp
SELECT *
FROM scott.dept;
4 linhas inserido.
COMMIT;
Commit concluído.

Delete all data from a table after selecting all data from the same table

All i want is to select all rows from a table and once it is selected and displayed, the data residing in table must get completely deleted. The main concern is that this must be done using sql only and not plsql. Is there a way we can do this inside a package and call that package in a select statement? Please enlighten me here.
Dummy Table is as follows:
ID NAME SALARY DEPT
==================================
1 Sam 50000 HR
2 Max 45000 SALES
3 Lex 51000 HR
4 Nate 66000 DEV
Any help would be greatly appreciated.
select * from Table_Name;
Delete from Table_Name
To select the data from a SQL query try using a pipelined function.
The function can define a cursor for the data you want (or all the data in the table), loop through the cursor piping each row as it goes.
When the cursor loop ends, i.e. all data has been consumed by your query, the function can perform a TRUNCATE table.
To select from the function use the following syntax;
SELECT *
FROM TABLE(my_function)
See the following Oracle documentation for information pipelined functions - https://docs.oracle.com/cd/B28359_01/appdev.111/b28425/pipe_paral_tbl.htm
This cannot be done inside a package, because " this must be done using sql only and not plsql". A package is PL/SQL.
However it is very simple. You want two things: select the table data and delete it. Two things, two commands.
select * from mytable;
truncate mytable;
(You could replace truncate mytable; with delete from mytable;, but this is slower and needs to be followed by commit; to confirm the deletion and end the transaction.)
Without pl/sql it's not possible.
Using pl/sql you can create a function which will populate a row, and then delete
Here is example :
drop table tempdate;
create table tempdate as
select '1' id from dual
UNION
select '2' id from dual
CREATE TYPE t_tf_row AS OBJECT (
id NUMBER
);
CREATE TYPE t_tf_tab IS TABLE OF t_tf_row;
CREATE OR REPLACE FUNCTION get_tab_tf RETURN t_tf_tab PIPELINED AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
FOR rec in (select * from tempdate) LOOP
PIPE ROW(t_tf_row(rec.id));
END LOOP;
delete from tempdate ; commit;
END;
select * from table(get_tab_tf) -- it will populate and then delete
select * from tempdate --you can check here result of deleting
you can use below query
select * from Table_demo delete from Table_demo
The feature you seek is SERIALIZABLE ISOLATION LEVEL. This feature enables repeatable reads, which in particular guarantee that both SELECTand DELETEwill read and process the same identical data.
Example
Alter session set isolation_level=serializable;
select * from tempdate;
--- now insert from other session a new record
delete from tempdate ;
commit;
-- re-query the table old records are deleted, new recor preserved.

Insert deleted rows to new table in stored procedure

How can I insert the rows deleted in a delete statement into a new table within a stored procedure in DB2 SQL?
DB2 allows the following syntax to return the deleted rows:
select * from old table (
delete from my_table where foo > 1
)
For some reason, you can't just do an insert based on the results returned from that statement. However, you can use common table expressions as a kludgy workaround:
with deleted as (
select * from old table (delete from my_table where foo > 1)
)
select * from new table (insert into archive_table select * from deleted)
This has an unnecessary extra select statement that I don't want, but at least it works. Deleted rows get inserted into another table.
However, how can I do this within a stored procedure?
A stored procedure doesn't allow a bare select statement. I thought of putting it within a set statement:
set my_var = (
with deleted as (
select * from old table (delete from my_table where foo > 1)
)
select * from new table (insert into archive_table select * from deleted)
);
However, this fails because a common table expression is not allowed within such a statement.
Is there any way to do this within a stored procedure?
(The task can be done using some other method as a work-around. But I want to find out if it is possible to do it this way. If this is not possible, it seems like quite a dumb restriction.)
Update: I'm using DB2 9.7 LUW.
If you issue a select, you have to consume the result set somehow, whether it is in a procedure or another application. You can either run a dummy loop within the procedure, like:
for t in (with deleted as (
select * from old table (delete from my_table where foo > 1)
)
select * from new table (insert into archive_table select * from deleted)
) loop
null;
end loop;
or use an explicit cursor:
declare c1 cursor for with deleted as (
select * from old table (delete from my_table where foo > 1)
)
select * from new table (insert into archive_table select * from deleted);
...
open c1;
close c1;
Note that neither of these is tested.
Why dont you flip it around? You can INSERT from a SELECT, and you can SELECT the rows from a DELETE.

Is it possible to move a record from one table to another using a single SQL statement?

I need a query to move a record from one table to another without using multiple statements?
No, you cannot move records in one SQL statement. You have to use an INSERT followed by a DELETE statement. You should wrap these statements into a transaction, to make sure that the copy operation remains atomic.
START TRANSACTION;
INSERT INTO
new_table
SELECT
*
FROM
old_table
WHERE
some_field = 'your_criteria';
DELETE FROM old_table WHERE some_field = 'your_criteria';
COMMIT;
If you really want to do this in a single SQL statement, one way to accomplish this would be to create an "after delete" trigger on the source table that inserts the row into the target table. This way you can move the row from the source table to the target table simply by deleting it from the source table. Of course this will only work if you want to insert into target table for every delete on the source table.
DELIMITER $$
DROP TRIGGER IF EXISTS TR_A_DEL_SOURCE_TABLE $$
CREATE TRIGGER TR_A_DEL_SOURCE_TABLE AFTER DELETE ON SOURCE_TABLE FOR EACH ROW BEGIN
INSERT IGNORE INTO TARGET_TABLE(id,val1,val2) VALUES(old.id,old.va1,old.val2);
END $$
DELIMITER ;
So to move the row with id 42 from source table to target table:
delete from source_table where id = 42;
No - you might be able to do the INSERT in one nested statement, but you still need to remove the record.
There is no way to make it a single query, but if you HAVE to do it in a single query within an application you can create a Stored Procedure to do it for you.
DELIMITER $$
DROP PROCEDURE IF EXISTS `copydelete` $$
CREATE PROCEDURE `copydelete` (id INT)
BEGIN
INSERT INTO New_Table SELECT * from Old_Table WHERE Old_Table.IdField=id;
DELETE FROM Old_Table where IdField=id;
END $$
DELIMITER ;
Then you're new query is just
CALL copydelete(4);
Which will delete WHERE IdField=4;
Please note that the time delay between insert-select and delete can cause you to delete to much.
For a safe route you could use an update field:
update old_table set move_flag=1 where your_criteria
insert into ... select from ... where move_flag = 1
delete from old_table where move_flag=1
Or use a transaction which locks the old_table so no data can be added between insert... select and delete.