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.
Related
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
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';
EDITED
I have a problem with performing some PL/SQL code.
I have real table a. I want to take only elements with range<=100. I make a collection inside my PL/SQL based on that table. Then I want to perform SELECT operation on that collection. But I got a problem with it.
Prepared table (this is all for example, it's not a real problem. I just would like to know how can I select from collection in PL/SQL code block).
CREATE TABLE a (amount NUMBER);
INSERT INTO a VALUES (50);
INSERT INTO a VALUES (100);
INSERT INTO a VALUES (200);
And then I got this block:
DECLARE
TYPE aTable IS TABLE OF a%ROWTYPE;
aActual aTable;
temp NUMBER;
BEGIN
SELECT * BULK COLLECT INTO aActual
FROM a WHERE amount<=100;
SELECT SUM(amount) INTO temp FROM TABLE(aActual);
DBMS_OUTPUT.PUT_LINE(temp);
END;
But I got eroor PLS-00642 and ORA-22905.
What can I do? Why it doesn't work that way?
I'm on Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production version (according to SELECT * FROM V$VERSION;)
You can't do it because aTable is not a database table. (Yes I know it's defined with table of but that doesn't define a table. One of those things.)
To ask SQL to treat a collection as a database table you would use the table() construction:
select sum(amount) into temp from table(aActual);
although this will fail in your example due to scoping issues and you'll get the self-explanatory
PLS-00642: local collection types not allowed in SQL statements
For it to work, you'd need a schema-level type i.e. one created with create type:
create or replace type xyz as object (a integer, b varchar2(3), c date);
create or replace type xyz_tt as table of xyz;
Now type xyz_tt is in effect published to SQL and it can be used in SQL table() expressions.
As WilliamRobertson showed, you can't use a PL/SQL collection in a SQL query. You can loop over the collection and add each amount to your temp variable, initialising it to zero first:
temp := 0;
for i in 1..aActual.count loop
temp := temp + aActual(i).amount;
end loop;
DBMS_OUTPUT.PUT_LINE(temp);
I need to write a PL/SQL procedure to create tables that match the ones in another account(I have access to that account). They need to have same columns and types. Also, they need to be filled with the same data
Help me!
EDIT:
SQL> CREATE OR REPLACE PROCEDURE MakeTables
2 AS
3 BEGIN
4 EXECUTE IMMEDIATE
5 'CREATE TABLE Table1 AS (SELECT * FROM ANOTHER_ACCT.Table1);
6 CREATE TABLE Table2 AS (SELECT * FROM ANOTHER_ACCT.Table2);
7 CREATE TABLE Table3 AS (SELECT * FROM ANOTHER_ACCT.Table3);
8 CREATE TABLE Table4 AS (SELECT * FROM ANOTHER_ACCT.Table4)';
9 END;
10 /
Procedure created.
But when I run this I get this error:
SQL> BEGIN
2 MakeTables;
3 END;
4 /
BEGIN
*
ERROR at line 1:
ORA-00911: invalid character
ORA-06512: at "BS135.MAKETABLES", line 4
ORA-06512: at line 2
When you say, another "account", do you mean, another "user/schema"? If so, this can be simple. Go read/google about "oracle create table as select". This lets you create a table from a select statement, so you could issue a statement such as
create table new_table as select * from other_schema.old_table
You don't need any PL/SQL unless you wanted to automate the process for creating many tables. Then you could query the data dictionaries as a driver.
(also, please read on how to ask proper questions here: https://stackoverflow.com/questions/how-to-ask )
How can i make multiple, composite query in oracle?
for example this several queries in one step?
1
CREATE TABLE test (id NUMBER PRIMARY KEY, name VARCHAR2(30));
2
CREATE SEQUENCE test_sequence START WITH 1 INCREMENT BY 1;
3
CREATE OR REPLACE TRIGGER test_trigger
BEFORE INSERT
ON test
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT test_sequence.nextval INTO :NEW.ID FROM dual;
END;
4
INSERT INTO test (name) VALUES ('Jon');
5
INSERT INTO test (name) VALUES ('Meloun');
We solved it by wrapping each statement in an EXECUTE IMMEDIATE command inside a PL/SQL script:
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE test (id NUMBER PRIMARY KEY, name VARCHAR2(30))';
EXECUTE IMMEDIATE 'CREATE SEQUENCE test_sequence START WITH 1 INCREMENT BY 1';
-- etc
END;
By and large DDL statements have to executed one at a time. It is true that Oracle supports the CREATE SCHEMA syntax, but this only permits the creation of multiple tables and/or views plus grants on them. It doesn't include ancilliary objects such as sequences or triggers.
You could turn the two inserts into a single statement like this:
INSERT INTO test (name)
select 'Jon' from dual
union all
select 'Meloun' from dual
/
This might be more trouble than its worth, but I suppose it does give you a simple transactionality: it inserts either all the rows or none of them.