PLSQL Procedure to Truncate and Re-populate table - sql

I have some tables and views in my schema and I am trying to create a stored procedure that will take in 2 parameters (table_name, view_name) to Truncate a table and re-populate it from a view.
Here is the code I have for the procedure:
CREATE OR REPLACE
PROCEDURE PROC_NAME (TABLE_NAME IN VARCHAR2, VIEW_NAME IN VARCHAR2)
IS
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE TABLE_NAME';
EXECUTE IMMEDIATE 'INSERT INTO TABLE_NAME
SELECT * FROM VIEW_NAME';
END;
/
Now when I run the following code:
BEGIN
PROC_NAME('SOME_TABLE', 'SOME_VIEW');
END;
/
I get the following error:
ORA-00942: table or view does not exist
ORA-06512: at "SCHEMA.PROC_NAME", line 4
ORA-06512: at line 2
00942. 00000 - "table or view does not exist"
What do you guys think is the issue?
Thanks in advance!

Try:
CREATE OR REPLACE
PROCEDURE PROC_NAME (TABLE_NAME IN VARCHAR2, VIEW_NAME IN VARCHAR2)
IS
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE '||TABLE_NAME;
EXECUTE IMMEDIATE 'INSERT INTO '||TABLE_NAME||'
SELECT * FROM '||VIEW_NAME;
END;
/
Your basic problem is that you had passed the parameters correctly but had not used them in the procedure. The fix was to used the the concatenation operaterator || in the strings used by EXECUTE IMMEDIATE to combine the the parameters into the string being executed.
An additional option is to use DELETE FROM rather than TRUNCATE TABLE. When Oracle first implemented Materialised Views, which is a grown up version of what you are trying to achieve, they made the same mistake. TRUNCATE TABLE is very quick but in the Oracle implementation it is a DDL (Data Definition Language) statement which means it will complete with an implicit commit. Therefore, for a period of time until the INSERT completes (and is committed), your table will be empty. If Oracle thought it important enough to change their underlying technique, then you should consider doing the same.
If you do not change to the DELETE technique then you should adding a COMMIT at the end of your procedure. The use of TRUNCATE TABLE will guarantee the removal of the data is committed, so if your INSERT succeeds then you should also commit that statement.
My reference to Materialised Views is relevant as it is a potential a built-in replacement for what you are trying to write for yourself. The problem with it is that it has so many bells and whistles that it is difficult to find an article on how to use it in your simple use case. I would welcome a comment referencing such an article.

Related

Oracle: Setting Session Parameters in Packages and Procedures

I'm a SQL Server DBA currently getting up to speed on Oracle. I'm trying to create something very similar to sp_WhoIsActive for SQL Server but for Oracle without reinventing the wheel. Essentially all I'm doing is selecting some values from v$session and inserting them into a table (poor man's ASH/AWR).
It would seem that in Oracle 12.1, there's a bug when querying dictionary views where it can take forever due to bad parsing logic (Bug 22225899 : SLOW PARSE FOR COMPLEX QUERY). The work-around is to set a session parameter:
alter session set "_optimizer_squ_bottomup"=false;
In T-SQL, I could very easily execute a stored procedure in-session and set this variable at runtime. However in Oracle, it wouldn't seem thats the case.
Sample Code:
CREATE OR REPLACE PROCEDURE SP_DB_ACTIVITY
(
v_temp NUMBER :=1
) IS
BEGIN
alter session set "_optimizer_squ_bottomup"=false;
INSERT INTO SY_DB_ACTIVITY
SELECT
<fields>
FROM
v$session;
commit;
When I run this, I get the error:
"PLS-00103: Encountered symbol 'ALTER' when expecting one of the following..."
Right now, the only way I know how to do this is via a utility like SQL Plus that initiates an interactive user session. Can anyone give me some direction as to how Oracle handles this situation? I'd like to bundle this up into a SP or a Package and then call it from Oracle Scheduler.
Hre is a simple example how to execute alter session inside of the procedure:
CREATE PROCEDURE SP_DB_ACTIVITY IS
BEGIN
EXECUTE IMMEDIATE 'alter session set "_optimizer_squ_bottomup"=false';
END;
/
Here is the way you can combine that with your select and insert statement:
CREATE OR REPLACE PROCEDURE SP_DB_ACTIVITY
(v_temp IN number) AS
v_Id NUMBER;
BEGIN
EXECUTE IMMEDIATE 'alter session set "_optimizer_squ_bottomup"=false';
SELECT 1
INTO v_Id
FROM dual;
INSERT INTO SY_DB_ACTIVITY (id) VALUES(v_Id);
END SP_DB_ACTIVITY;
/
Here is a small DEMO where you can see what will procedure do when you call it and how you can call it. Also, in this example you are calling procedure with and IN parameter. So you can use that parameter for something and in the example above is the procedure without any parameters...
You can also, of course, insert into table directly:
CREATE OR REPLACE PROCEDURE SP_DB_ACTIVITY
(v_temp IN number) AS
v_Id NUMBER;
BEGIN
EXECUTE IMMEDIATE 'alter session set "_optimizer_squ_bottomup"=false';
INSERT INTO SY_DB_ACTIVITY(id)
select 1
from dual;
END SP_DB_ACTIVITY;
/

Deleting a non-unique procedure on DB2

In my attempts to edit a procedure using the line
CREATE OR DROP PROCEDURE
I have created two procedures with the same name, how can I delete them?
The error I receive whenever I attempt to drop it is
Reference to Rountine BT_CU_ODOMETER was made without a signature, but the routine is not unique in its schema.
SQLSTATE = 42725
I am using DB2
Assuming this is DB2 for LUW.
DB2 allows you to "overload" procedures with the same name but different number of parameters. Each procedure receives a specific name, which can be provided by you or generated by the system and which will be unique.
To determine the specific names of your procedures, run
SELECT ROUTINESCHEMA, ROUTINENAME, SPECIFICNAME FROM SYSCAT.ROUTINES
WHERE ROUTINENAME = 'BT_CU_ODOMETER'
You can then drop each procedure individually:
DROP SPECIFIC PROCEDURE <specific name>
In case you need to drop all the overloads of a given procedure name, here's a handy script based on mustaccio's answer
BEGIN
FOR rec AS
SELECT SPECIFICNAME, ROUTINETYPE
FROM SYSCAT.ROUTINES
WHERE ROUTINENAME = 'ROUTINE_NAME'
DO
IF rec.ROUTINETYPE = 'P' THEN
EXECUTE IMMEDIATE 'DROP SPECIFIC PROCEDURE ' || rec.SPECIFICNAME;
ELSE
EXECUTE IMMEDIATE 'DROP SPECIFIC FUNCTION ' || rec.SPECIFICNAME;
END IF;
END FOR;
END
PROBLEM
When multiple stored procedures are created with the same name but with a different number of parameters, then the stored procedure is considered overloaded. When attempting to drop an overloaded stored procedure using the DROP PROCEDURE statement, the following error could result:
db2 drop procedure SCHEMA.PROCEDURENAME
DB21034E The command was processed as an SQL statement because it was not valid Command Line Processor command. During SQL processing it returned: SQL0476N Reference to routine "SCHEMA.PROCEDURENAME" was made without a signature, but the routine is not unique in its schema. SQLSTATE=42725
CAUSE
The error is returned because the stored procedure is overloaded and therefore the procedure is not unique in that schema. To drop the procedure you must specify the data types that were specified on the CREATE PROCEDURE statement or use the stored procedure's specific name per the examples below.
SOLUTION
In order to drop an overloaded stored procedure you can use either of the following statements:
db2 "DROP PROCEDURE procedure-name(int, varchar(12))"
db2 "DROP SPECIFIC PROCEDURE specific-name"
Note: The specific-name can be identified by selecting the SPECIFICNAME column from syscat.routines catalog view.
A procedure can be dropped like:
DROP PROCEDURE INORUP RESTRICT;
The parameter RESTRICT is required. It avoids dropping a procedure used by a trigger. The procedure package is dropped.
Packages and plans calling the procedure are invalidated.

Oracle Sql : Procedure which can create temporary tables inside it

I am new to Oracle Sql and facing an issue :
I want to create a temporary table inside procedure .
LIKE:
CREATE PROCEDURE P
AS
BEGIN
CREATE TEMPORARY TABLE A(ID int);
END P;
BUT THIS IS GIVING ME AN ERROR
How Can I Create a temporary table inside procedure.
I have seen other answers on stackoverflow but that doesn't answer my question properly
Can you please help me out ?
Why do you want to create a temporary table in a stored procedure in the first place?
It is relatively common to create temporary tables in other databases (SQL Server and MySQL, for example). It is very, very rare to do the same thing in Oracle. In almost every case where you are tempted to create a temporary table in Oracle, there is a better architectural approach. There is a thread over on the DBA stack that discusses alternatives to temporary tables and why they are not commonly needed in Oracle.
Programmatically, you can create objects using dynamic SQL
CREATE OR REPLACE PROCEDURE dont_do_this
AS
BEGIN
EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE a( id INTEGER )';
END;
If you create a temporary table dynamically, however, every reference to that table will also need to be via dynamic SQL-- you won't be able to write simple SELECT statements against the table. And the definition of a temporary table in Oracle is global so it is visible to every session. If you have two different sessions both trying to create the same table, the second session will get an error. If you expect the table to have a different definition in different sessions, you've got even more problems.
You could use Dynamic SQL with EXECUTE IMMEDIATE:
CREATE OR REPLACE
PROCEDURE p
AS
BEGIN
EXECUTE IMMEDIATE 'CREATE TEMPORARY TABLE A(id NUMBER)...etc';
END p;
Edit: Obviously you'll have to ensure your syntax is correct within the EXECUTE IMMEDIATE statement.
Hope it helps.
You must declare your procedure as:
create or replace PROCEDURE MYPROCEDURE AUTHID CURRENT_USER IS
script varchar(4000);
BEGIN
script:= 'CREATE GLOBAL TEMPORARY TABLE BNMCODIAGNOSTICASSOCIE_TEMP
ON COMMIT PRESERVE ROWS
as select ........';
EXECUTE IMMEDIATE script;
commit;
END;

Create table and call it from sql

I have a PL/SQL function which creates a new temporary table. For creating the table I use execute immediate. When I run my function in oracle sql developer everything is ok; the function creates the temp table without errors. But when U use SQL:
Select function_name from table_name
I get an exceptions:
ORA-14552: cannot perform a DDL, commit or rollback inside a query or DML
ORA-06512: at "SYSTEM.GET_USERS", line 10
14552. 00000 - "cannot perform a DDL, commit or rollback inside a query or DML "
*Cause: DDL operations like creation tables, views etc. and transaction
control statements such as commit/rollback cannot be performed
inside a query or a DML statement.
Update
Sorry, write from tablet PC and have problems with format text. My function:
CREATE OR REPLACE FUNCTION GET_USERS
(
USERID IN VARCHAR2
)
RETURN VARCHAR2
AS
request VARCHAR2(520) := 'CREATE GLOBAL TEMPORARY TABLE ';
BEGIN
request := request || 'temp_table_' || userid ||
'(user_name varchar2(50), user_id varchar2(20), is_administrator varchar2(5)') ||
' ON COMMIT PRESERVE ROWS';
EXECUTE IMMEDIATE (request);
RETURN 'true';
END GET_USERS;
The error is explicit:
ORA-14552: cannot perform a DDL, commit or rollback inside a query or DML
In Oracle, you can't commit inside a query. A likely explanation is that it would make no sense since a query in Oracle is atomic (either succeeds entirely or makes no change) and this couldn't work if you commit in the middle of a DML. For a select query, all rows must be returned from a single logical point-in-time and if you commit in the middle of a select you would have inconsistent results.
Since DDL in Oracle issue an implicit commit, you can't make DDL inside a query.
This should not be a problem in your case though: SQL server-like temporary tables are not equivalent to the GLOBALLY temporary table in Oracle. There is a reason why temp tables in Oracle are always prefixed with GLOBALLY: they are visible to all sessions although the data in the temporary table is private to each session.
In Oracle creating a temporary table is a relatively expensive operation and you should not create individual temporary tables: all sessions should that do the same job should use the same common structure. Instead of creating multiple temporary tables, in Oracle you should create the table once and reuse it in all procedures. If you are going to need it later, why drop it?
In any case, if you decide to do multiple DDL that depend upon a SELECT, you could do it in a PLSQL block instead of a SELECT query:
DECLARE
l VARCHAR2(100);
BEGIN
FOR cc IN (SELECT col FROM tab) LOOP
l := create_temp_table(cc.col);
END LOOP;
END;
I tested below solution on Oracle 10g XE, it works for me.
Create function:
CREATE OR REPLACE FUNCTION GET_USERS
(
USERID IN VARCHAR2
)
RETURN VARCHAR2
AS
request VARCHAR2(255) := 'CREATE GLOBAL TEMPORARY TABLE ';
BEGIN
request := request || 'temp_table_' || userid ||
'(user_name varchar2(50), user_id varchar2(20), is_administrator varchar2(5))' ||
' ON COMMIT PRESERVE ROWS';
EXECUTE IMMEDIATE request;
RETURN 'true';
END GET_USERS;
Run function:
SET SERVEROUTPUT ON
DECLARE
RESULT VARCHAR(255);
BEGIN
RESULT:=gET_USERS('ADMIN3');
dbms_output.put_line(result);
END;
and select from temporary table:
SELECT * FROM temp_table_admin3;

Create/alter from SQL stored procedure

I want to call create table/ alter table command from a procedure. Is it possible?
My requirement is to change the datatype of a column in all tables. So, I am just getting the column name from user_tab_cols. Now I want to create a temp table which requires create statement .. but i am unable to use that within a proc.
Can anyone please help me out?
I presume from the reference to USER_TAB_COLUMNS that this is Oracle. ALTER and CREATE statements are DDL, which we cannot execute directly in PL/SQL. However, there are a couple of ways around this restriction: EXECUTE IMMEDIATE and DBMS_UTILITY.EXEC_DDL(). I will use EXECUTE IMMEDIATE in the following example.
begin
for lrec in ( select table_name from user_tab_columns
where column_name = 'UNIVERSAL_COLUMN_NAME')
loop
execute immediate 'alter table '||lrec.table_name||
' modify UNIVERSAL_COLUMN_NAME varchar2(255)';
end loop;
end;
Note that the usual restrictions apply: the new datatype has to be compatible with the existing datatype (unless the column is empty), and things are trickier with some specilaized datatypes like CLOBs.
edit
I haven't addressed the CREATE TABLE statement. The principle is the same, it is just longer to type out. Besides, I am not entirely clear how it applies to your prior requirement to change the datatype of those columns.
you can generate the query as string and execute it with 'exec' keyword.