How to create tables separately using PostgreSQL? - sql

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

Related

Oracle Making local tables using dynamic sql?

How to use local temporary table or alternative using dynamic SQL in Oracle?
In SQL Server, to select all the columns from a table called dbo.2019 into a local temporary table called x:
CREATE TABLE #x (a int)
DECLARE #FY varchar(4) = Year(date())
EXEC ('SELECT * into #x FROM dbo.'+#FY)
The reason why I want to do this is because I want to use all kinds of information (besides year, like values from other tables or whatever) to build very complicated queries in a manner that does not require lots of bizarre subqueries.
The limitation of SQL Server is that you must make your temporary tables first, or you have to use global ones, that people could write over.
As you know, Oracle <> MS SQL Server. The latter uses temporary tables a lot. Oracle - usually/mostly - doesn't need them.
But, for your specific problem ("very complicated queries" and stuff), that maybe isn't a bad idea. Depending on your Oracle database version, there are global temporary tables (and - in most recent versions, private ones). How do they work? You create them once and use many times, which means that you should NOT create them dynamically. Data stored within is visible to you only, although many users can use it simultaneously. Furthermore, data is kept during the whole session (if you opt to create them with the on commit preserve rows option) or during transaction (on commit delete rows).
For example:
SQL> create global temporary table gtt_test
2 (id number,
3 name varchar2(20))
4 on commit delete rows;
Table created.
SQL>
You can index them:
SQL> create index i1_gtt on gtt_test (id);
Index created.
SQL>
and do anything you want. Once your session (or transaction) is over, puff! they are empty, not data is permanently stored within. When you have to do the same job tomorrow, the table is still here, waiting for you. No dynamic create/drop/create/drop (or whatever you thought you should do).
So: create it once, use it any time you need.
If it must be dynamically, beware of this:
SQL> declare
2 l_cnt number;
3 begin
4 -- check whether table already exists
5 select count(*)
6 into l_Cnt
7 from user_tables
8 where table_name = 'TEST';
9
10 -- yes, it exists - drop it first
11 if l_cnt = 1 then
12 execute immediate 'drop table test';
13 end if;
14 -- now create it
15 execute immediate 'create table test (id number, name varchar2(20))';
16
17 -- insert some rows
18 insert into test (id, name) values (1, 'Littlefoot');
19 end;
20 /
insert into test (id, name) values (1, 'Littlefoot');
*
ERROR at line 18:
ORA-06550: line 18, column 15:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 18, column 3:
PL/SQL: SQL Statement ignored
SQL>
Although I'm first checking whether the table exists (so that I'd drop it first and then recreate it), the whole PL/SQL block failed because I'm trying to use a table that doesn't exist yet (at compile time).
So, if I'd want to use that test table, any operation - within the same PL/SQL block - should also become dynamic, and that's horrible: ugly to write, difficult to debug, beware of single quote problems ... you'd really want to avoid that.
Once again: I'd suggest you not to do that dynamically.

Create a global temporary table in Oracle 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;

PostgreSQL FOUND for CREATE TABLE statements

I am creating a function that will create a new table and insert informations about that table into other tables.
To create that table I am using the
CREATE TABLE IF NOT EXISTS
statement. Sadly it does not update the FOUND special variable in PostgreSQL nor can i find any other variable that would be updated.
Is there any way in PL/PGSQL to know whether that statement created a table or not?
The target of it is to not to double the informations in the other tables.
You may use CREATE TABLE AS in combination with ON_ERROR_ROLLBACK:
BEGIN;
-- Do inital stuff
\set ON_ERROR_ROLLBACK on
CREATE TABLE my_table AS
SELECT id, name FROM (VALUES (1, 'Bob'), (2, 'Mary')) v(id, name);
\set ON_ERROR_ROLLBACK off
-- Do remaining stuff
END;
To put it bluntly, with \set ON_ERROR_ROLLBACK on postgres will create a savepoint before each statement and automatically rollback to this savepoint or releasing it depending on the success of that statement.
The code above will execute initial and remaining stuff even if the table creation fails.
No, there are not any information if this command created table or not. The found variable is updated after query execution - not after DDL command. There is guaranteed so after this command, the table will be or this command fails to an exception.

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.

Oracle Merge Sql with insert in destination and delete from source

I have a Scenario where i have 2 Tables 1st is source(my_data) and 2nd is destination(my_data_backup),I want to some kind of archiving of actual data and move that data into backup table on the daily basis and delete from source table using Merge SQL in oracle.
i.e.
my_data and my_data_backup both have same schema
my_data table contains 10 rows and my_data_backup contains 0 rows i want to insert 10 records into my_data_backup and delete those records from my_data.
MERGE is useful to do manipulation on the destination table, not source.
You can use an anonymous PLSQL block:
begin
delete from my_data_backup;
insert into my_data_backup
select *
from my_data;
delete from my_data;
commit;
exception
when others then
rollback;
-- handle here
end;
/
You can also put the above in a procedure and call the procedure.
You can think about using truncate statement instead of delete which will be faster when the table size is larger but be careful that it, being a DDL, will do an implicit commit.
execute immediate 'truncate table tablename';