Trying to run consecutive queries in Oracle - oracle

I am creating a temp table
CREATE TABLE TMP_PRTimeSum AS
SELECT DISTINCT p.employee,
SUM(p.wage_amount) AS wage_sum,
SUM(p.hours) AS hour_sum
I then want to do a select from that temp table and show the results. I want to do it all in one query. It works if I run them as two separate queries. Is there any way to do this in one query?

CREATE GLOBAL TEMPORARY TABLE a
ON COMMIT PRESERVE ROWS
AS
select * from b;
(add where 1=0 too if you didn't want to initially populate it for the current session with all the data from b).

Is there any way to do this in one query?
No, you need to use one DDL statement (CREATE TABLE) to create the table and one DML statement (SELECT) to query the newly created table.
If you want to be really pedantic then you could do it in a single PL/SQL statement:
DECLARE
cur SYS_REFCURSOR;
v_employee VARCHAR2(20);
v_wage NUMBER;
v_hours NUMBER;
BEGIN
EXECUTE IMMEDIATE
'CREATE TABLE TMP_PRTimeSum (employee, wage_sum, hour_sum) AS
SELECT P.EMPLOYEE,
SUM(P.WAGE_AMOUNT),
SUM(P.HOURS)
FROM table_name p
GROUP BY p.Employee';
OPEN cur FOR 'SELECT * FROM TMP_PRTimeSum';
LOOP
FETCH cur INTO v_employee, v_wage, v_hours;
EXIT WHEN cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_employee || ', ' || v_wage || ', ' || v_hours);
END LOOP;
END;
/
db<>fiddle here
However, you are then wrapping the two SQL statements in a PL/SQL anonymous block so, depending on how you want to count it then it is either one composite statement, two SQL statements or three (2 SQL and 1 PL/SQL) statements.

Related

Procedure to Create Backup Table For multiple table each having different Where condition

Create or replace procedure PROC AS
V_TABLE_NAME VARCHAR2(255);
V_LIST SYS_REFCURSOR;
DATE_VALUE_INS VARCHAR2(10);
BEGIN
DATE_VALUE_INS:=TO_CHAR(SYSDATE,'YYMMDD');
OPEN V_LIST FOR
SELECT NAME FROM DW.table_name_list ;
LOOP
FETCH V_LIST
INTO V_TABLE_NAME;
EXIT WHEN V_LIST%NOTFOUND;
EXECUTE IMMEDIATE 'CREATE TABLE Schema.'||V_TABLE_NAME||'_'||DATE_VALUE_INS||' AS SELECT * FROM DW.'||V_TABLE_NAME;
END LOOP;
CLOSE V_LIST;
end;
I have created this Proc which takes value from a table which has Table_name and create Backup using Execute Immediate.
Now the requirement has changed that i only need to create backup for partial records (i.e. where clause on each table )
I have 6 tables as such .
New Approach i am thinking is :
EXECUTE IMMEDIATE 'CREATE TABLE Schema.'||V_TABLE_NAME||'_'||DATE_VALUE_INS||' AS SELECT * FROM DW.'||V_TABLE_NAME where some condition;
But the problem becomes all 6 have different column to filter on.
My Ask is How should I change my design of proc to Adjust this new Requirement.
6 tables? Why bother? Create a procedure which - depending on table name passed as a parameter - in IF-THEN-ELSE runs 6 different CREATE TABLE statements.
On the other hand, another approach would be to create backup tables in advance (at SQL level), add BACKUP_DATE column to each of them, and - in procedure - just perform INSERT operation which doesn't require dynamic SQL at all.
For example:
create table emp_backup as select * from emp where 1 = 2;
alter table emp_backup add backup_date date;
create or replace procedure p_backup (par_table_name in varchar2) is
begin
if par_table_name = 'EMP' then
insert into emp_backup (empno, ename, job, sal, backup_date)
select empno, ename, job, sal, trunc(sysdate)
from emp
where deptno = 20; --> here's your WHERE condition
elsif par_table_name = 'DEPT' then
insert into dept_backup (...)
select ..., trunc(sysdate)
from dept
where loc = 'DALLAS';
elsif ...
...
end if;
end;
/
Doing so, you'd easier access backup data as you'd query only one table, filtered by BACKUP_DATE. That's also good if you have to search for some data that changed several days ago, but you don't know exact day. What would you rather do: query 10 tables (and still not find what you're looking for), or query just one table and find that info immediately?

How to write procedure to copy data from one table to another table

Hello I have this situation
I have a table with 400 millions of records I want to migrate some data to another table for get better performance.
I know that the process could be slow and heavy and I want to execute something like this
INSERT INTO TABLLE_2 SELECT NAME,TAX_ID,PROD_CODE FROM TABLE_1;
But I want this in a procedure that iterates the table from 50000 to 50000 records.
I saw this but it DELETE the rows and the target is SQLServer and not Oracle
WHILE 1 = 1
BEGIN
DELETE TOP (50000) FROM DBO.VENTAS
OUTPUT
Deleted.AccountNumber,
Deleted.BillToAddressID,
Deleted.CustomerID,
Deleted.OrderDate,
Deleted.SalesOrderNumber,
Deleted.TotalDue
INTO DATOS_HISTORY.dbo.VENTAS
WHERE OrderDate >= '20130101'
and OrderDate < '20140101'
IF ##ROWCOUNT = 0 BREAK;
END;
If it's just for a one-time migration, don't paginate/iterate at all. Just use a pdml append insert-select:
BEGIN
EXECUTE IMMEDIATE 'alter session enable parallel dml';
INSERT /*+ parallel(8) nologging append */ INTO table_2
SELECT name,tax_id,prod_code from table_1;
COMMIT;
EXECUTE IMMEDIATE 'alter session disable parallel dml';
END;
Or if the table doesn't yet exist, CTAS it to be even simpler:
CREATE OR REPLACE TABLE table_2 PARALLEL (DEGREE 8) NOLOGGING AS
SELECT name,tax_id,prod_code FROM table_1;
If you absolutely must iterate due to other business needs you didn't mention, you can use PL/SQL for that as well:
DECLARE
CURSOR cur_test
IS
SELECT name,tax_id,prod_code
FROM table_1;
TYPE test_tabtype IS TABLE OF cur_test%ROWTYPE;
tab_test test_tabtype;
var_limit integer := 50000;
BEGIN
OPEN cur_test;
FETCH cur_test BULK COLLECT INTO tab_test LIMIT var_limit;
LOOP
-- do whatever else you need to do
FORALL i IN tab_test.FIRST .. tab_test.LAST
INSERT INTO table_2
(name,tax_id,prod_code)
VALUES (tab_test(i).name, tab_test(i).tax_id, tab_test(i).prod_code);
COMMIT;
EXIT WHEN tab_test.COUNT < var_limit;
FETCH cur_test BULK COLLECT INTO tab_test LIMIT var_limit;
END LOOP;
CLOSE cur_test;
END;
That won't be nearly as fast, though. If you need even faster, you can create a SQL object and nested table type instead of the PL/SQL types you see here and then you can do a normal INSERT /*+ APPEND */ SELECT ... statement that will use direct path. But honestly, I doubt you really need iteration at all..

How to create a cursor within for loop of PL/SQL Code and bulk collect results into table

I have a database with many users that have the same table (by same I mean same columns but different data). I would like to run the same query on these tables and bulk collect the results into a temporary table or any other way of viewing.
So far I have the code shown below:
DECLARE
TYPE PDTABLE_12SEGTBL IS TABLE OF MTO_SG2420.PDTABLE_12%ROWTYPE;
COLLECTIONTBL PDTABLE_12SEGTBL;
LoopIteration pls_integer;
CompTblName varchar2(61);
CURSOR MTO_Cursor IS
SELECT owner, table_name
FROM ALL_TABLES
WHERE OWNER LIKE 'MTO_K%'
AND TABLE_NAME = 'PDTABLE_12';
BEGIN
LoopIteration :=1;
FOR item IN MTO_Cursor
LOOP
CompTblName := item.owner || '.pdtable_12';
DBMS_OUTPUT.PUT_LINE('Loop Iteration ' || LoopIteration || ' CompTblName' || CompTblName);
LoopIteration := LoopIteration + 1;
END LOOP;
END;
The tables that I would like to run the query on look like this:
MTO_K01.pdtable_12
MTO_K02.pdtable_12
MTO_K03.pdtable_12
MTO_K04.pdtable_12
MTO_K05.pdtable_12
In the CompTblName variable, I store the complete table name including username successfully through each iteration.
My question is how can I add a query to the code above that runs a select statement on the variable CompTblName and pushes the results into the table that I created (COLLECTIONTBL). I searched in this forum and other places and saw that I can do this using fetch command. However, fetch command needs to be placed within a cursor which gives an error every time I place it in a loop. It is important to note that I would like to concatenate the results from all iterations into COLLECTIONTBL.
You need to use execute immediate statement, which allows to create and run dynamic SQL:
FOR item IN MTO_Cursor LOOP
CompTblName := item.owner || '.pdtable_12';
execute immediate 'insert into COLLECTIONTBL select * from ' || CompTblName;
END LOOP;

Back up Oracle with script (Insert statements, DDL)

I would like to backup part of a database with Insert and DDL statements with output similar to what you can get with TOAD or SQL Developer, but from a script.
Details:
This is to be able to see changes and differences with source control.
We are using SQL Developer, LINUX tools and Python (With Oracle).
try this? Change settings as per your need.
Exporting Schema:
1) Run following in command prompt (not mandatory though)
SET NLS_LANG AMERICAN_AMERICA.UTF.8
2) Once above is set run the below, run the below. Change username/password/schemaname, path to export
exp userid=<schemaname>/<pwd>#<dbname or SID> file=<localpath\1.dmp>
log=<localpath\2.log> buffer=1000000000 feedback=25000
direct=y recordlength=64000 owner=<schemaname>
There must be tools for what you are trying to do (other than TOAD and PL/SQL).
This is a great project you have if you intend to write the code for this. You must work as follows:
For DDLs
. Write a script that list all the objects you need to extract; this will be tables, schemas, procedures, functions:
for x in (
select * from dba_objects where object_type in ('PACKAGES', 'TABLES', '>others>') loop
--- things to do with x.>columns<
end loop;
for u in (
select * from dba_users where username in ( <list of needed schemas> ) loop
)
--- things to do with users
end loop;
. Then from above list, you have Oracle function dbms_metadata.get_ddl that generates the DDL. e.g. with users:
begin
for u in (select * from dba_users where <list of needed schemas>
order by username)
loop
dbms_metadata.get_ddl('USER', u.username);
end loop;
end;
For the inserts
(this could take time to generate on databases with large tables (thousands of rows is already a lot for my approach).
I advise you create a generic function that generates the insert statements. This is rather simple as long as it does not include BLOB and CLOB. You take each column of each table.
Remember to generate the inserts in an orderly way, otherwise the comparison will not be possible.
The generic function should take a table as input. From the table name, it gets the columns from dba_tables, then selects the table.
--- plsql code below lists the columns of the tables
begin
for t in (select * from dba_tables order by table_name) loop
for c in (select * from dba_tab_columns where table_name=t.table_name and owner=t.owner order by owner, table_name, column_name) loop
dbms_output.put_line(c.table_name||'.'||c.column_name);
end loop;
end loop;
end;
Here is how you could collect the values in an array, then loop on this array to display the insert statements as text (with dbms_output.put_line)
declare
type t_v is table of varchar2(32000);
t_vchars t_v;
v_ins varchar2(2000):=' ';
v_sel varchar2(2000):=' ';
v_res varchar2(4030):=' ';
begin
for t in (select * from dba_tables order by table_name) loop
v_ins :='insert into '||t.table_name||'(';
v_sel :=' select ';
for c in (select * from dba_tab_columns where table_name=t.table_name and owner=t.owner) loop
v_ins:= v_ins||c.column_name||',';
v_sel:= v_sel||'''''''||'||c.column_name||'||'''''',';
end loop;
-- remove last comma
v_ins:= substr(v_ins,1,length(v_ins)-1);
v_sel:= substr(v_sel,1,length(v_sel)-1);
v_ins:= v_ins||')';
v_sel:= v_sel||' from dual';
v_res:= 'select '''||v_ins||v_sel||''' from '||t.table_name;
-- above, you still need to insert an order by, but I let you choose which solution
execute immediate v_res bulk collect into t_vchars;
FOR i in 1..t_vchars.count LOOP
dbms_output.put_line(t_vchars(i)||';');
END LOOP;
end loop;
end;
After you're done and want to test the whole script.
One tricky thing when you want to test and run the inserts is if you have foreign key. You must deactivate them before inserting, otherwise you need to do the inserts in a ordered way, which is not easy to do.
for c in (select * from dba_constraints)
loop
--- disable constraint
end loop;
Then after inserts are done
for c in (select * from dba_constraints)
loop
--- enable constraint
end loop;

Oracle - drop multiple table in a single query

I have fifty tables in a database, in that I need only six tables.
How can I delete remaining tables by one single query?
You can generate a list of DROP TABLE commands with the query below:
SELECT 'DROP TABLE ' || table_name || ';' FROM user_tables;
After that you remove your six tables you want to keep and execute the other commands. Or you add a WHERE table_name NOT IN (...) clause to the query.
Hope it helps.
Use something like this, as there is no direct command or way in oracle to do this
begin
for rec in (select table_name
from all_tables
where table_name like '%ABC_%'
)
loop
execute immediate 'drop table '||rec.table_name;
end loop;
end;
/
To expand on the answer,
for Oracle 10 versions and later, dropped tables are not deleted permanently, but moved to recycled bin. To truly delete tables need to add optional parameter PURGE.
Expanding on the accepted answer :
SELECT 'DROP TABLE ' || table_name || ' PURGE ;' DB_DEATH FROM user_tables;
First Run this query with table names that you want to keep.
SELECT CONCAT( 'DROP TABLE ', GROUP_CONCAT(table_name) , ';' )
AS statement FROM information_schema.tables
WHERE table_schema = 'mydatabase' AND table_name not in ('table1', 'table2', 'table3');
This query will give you DROP table query.
Select from the list on the left all the tables you want to drop. Drag and drop them in your worksheet. Select Object Name from the pop-up window
Press the Edit menu and select Replace.
Type in the find field the comma symbol ,
Type in the replace field the following text ;\ndrop table . Note that there is a space after the word table. Replace all.
Type drop table before your first table and ; after your last one.
You are ready to drop your tables.
DECLARE
TYPE bulk_array is table of ALL_TABLES.TABLE_NAME%type;
array bulk_array;
BEGIN
SELECT OWNER ||'.'|| TABLE_NAME BULK COLLECT
INTO array
FROM ALL_TABLES
WHERE TABLE_NAME LIKE '%%';--HERE U WILL PUT YOUR CONDITIONS.
FOR i IN array.FIRST..array.LAST LOOP
EXECUTE IMMEDIATE 'DROP TABLE '|| array(i) || ' PURGE'; --Specify PURGE if you want to drop the table and release the space associated
END LOOP;
END;
If the tables you want to keep are keep_tab1, keep_tab2, etc. or start with ASB..
use regexp_like. Here just to show an example with regexp_like
The loop continues even when an error occurs dropping one of the tables.You may
have to run this script multiple times as errors may occur when dropping a primary table without having dropped the referential tables first.
begin
for rec in (
select table_name from user_tables
where not
regexp_like(table_name, 'keep_tab1|keep_tab2|^ASB')
)
loop
begin
execute immediate 'drop table '||rec.table_name;
exception when others then
dbms_output.put_line(rec.table_name||':'||sqlerrm);
end;
end loop;
end;

Resources