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.
Related
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.
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.
I have more than 40 tables in RATOR_MONITORING schema for which the table name is starting from 'TEMP_'. I want to delete data from all such tables at once in a single query instead of using delete statement for each and every table. I dont even want to generate statements. I can create anonymous block if required but dont know how to do that. I tried below query but its not working.
Delete from RATOR_MONITORING_CONFIGURATION.'%TEMP_';
If you want to delete all the rows, then better use TRUNCATE, it will reset the high watermark. But remember, truncate is a DDL statement, and thus there will be an implicit commit; With DELETE you can commit manually after validation.
Although, I would not do that in a production environment. If it is something you are doing in test environment to build test data, then you could (ab)use EXECUTE IMMEDIATE.
For example, execute the following anonymous block as RATOR_MONITORING user:
DECLARE
v_sql VARCHAR2(100);
BEGIN
FOR i IN
(SELECT table_name FROM user_tables where table_name like 'TEMP%'
)
LOOP
v_sql := 'TRUNCATE TABLE '||i.table_name;
EXECUTE immediate v_sql;
END LOOP;
END;
/
By the way, using a good text editor, it won't take more than a minute to build DELETE/TRUNCATE statements and do it in pure SQL.
I am in the process of migrating a SQL Server database to Oracle, where I have to convert SQL Server procedure which uses special tables called INSERTED and DELETED in SQL Server.
As per my understanding these tables hold copies the data of last inserted/deleted records.
(find the msdn article here: http://msdn.microsoft.com/en-us/library/ms191300.aspx)
Are there any similar tables in Oracle to achieve this..? Please advise.
UPDATE:
Thanks for your answers and comments ,I think I need to explain the situation some more. Here is the full story to understand the real scenario;
Data base contains tables and shadow tables (shadow has an additional column).
When a table is updated same changes should be recorded in relevant shadow table with some additional data.
For this purpose they are having triggers for each table (these triggers copy data to relevant shadow table).
The above mentioned procedure generates these triggers dynamically for each and every table.
Now the real problem is I don't have the knowledge about the columns as triggers are dynamically generated for each table.
Basically I can’t get value like: NEW.col_1 or: OLD.col_1 as APC mentioned. Can I.?
Or else I have to write all those triggers manually using prefixes: NEW and: OLD rather than trying to generate them dynamically.
I am using Oracle 11g
Oracle triggers use pseudo-records rather than special tables. That is, instead of tables we can access the values of individual columns.
We distinguish pseudo-records in the affected table from records in (other) tables by using the prefixes :NEW and :OLD . Oracle allows us to declare our own names for these, but there is really no good reason for abandoning the standard.
Which column values can we access?
Action :OLD :NEW
------ ---- ----
INSERTING n/a Inserted value
UPDATING Superseded value Amended value
DELETING Deleted value n/a
You will see that :OLD is the same as the MSSQL table DELETED and :NEW is the same as table INSERTED
So, to trigger a business rule check when a certain column is updated:
create or replace trigger t23_bus_check_trg
before update on t23
for each row
begin
if :NEW.col_1 != :OLD.col_1 then
check_this(:NEW.col_1 , :OLD.col_1);
end if;
end t23_bus_check_trg;
There's a whole chapter on records in the PL/SQL Reference. Find out more.
There are many differences between Sql Server triggers and Oracle triggers. In Oracle, you can declare statement level or row level triggers. Sql Server only has statement level. In Oracle, you can declare before triggers or after triggers. Sql Server only has after triggers.
If you're going to be working with Oracle, although later versions have the compound trigger, get used to working with row level triggers. There you have the pseudo row designation of :old and :new, kinda like Deleted and Inserted except it's just the one row of data. It's like being in a cursor loop, something you can do in Sql Server, but cursor perform so poorly in Sql Server, developers go to great lengths to avoid them. They are commonly used in Oracle.
The general rule of thumb is this: if you need to examine the data and possibly alter it before it goes to the table, use a "before" trigger. If you want to perform an audit or logging procedure, use an "after" trigger.
The page I linked to above gives a lot of technical details, but it is absolutely atrocious at giving usable examples. For that, just google "oracle trigger tutorial" and you should get lots of handy, easy-to-learn-from examples.
Thanks for the answers and comments. here is the complete solution to my problem.If some one meet the exact problem this will help.
create or replace PROCEDURE CreateTrackingTriggers
(
-- take the target table and shadow user as agruments
v_TableName IN NVARCHAR2 DEFAULT NULL,
v_ShadowUser IN NVARCHAR2 DEFAULT 'SHADOW_USER'
)
AUTHID CURRENT_USER -- grant permission to create triggers
AS
v_TriggerName NVARCHAR2(500);
v_ColList NVARCHAR2(2000);
v_ColList_shadow NVARCHAR2(2000);
v_SQLCommand VARCHAR2(4000);
v_ColName NVARCHAR2(500);
v_ColSize NUMBER(10,0);
v_Prefix NVARCHAR2(500);
v_count NUMBER(1,0);
BEGIN
DECLARE
-- define a cursor to get the columns of the target table. order by COLUMN_ID is important
CURSOR Cols
IS SELECT COLUMN_NAME , CHAR_COL_DECL_LENGTH FROM USER_TAB_COLS
WHERE TABLE_NAME = upper(v_TableName) order by COLUMN_ID;
-- define a cursor to get the columns of the target shadow table order by COLUMN_ID is important
CURSOR Shadow_Cols
IS SELECT COLUMN_NAME , CHAR_COL_DECL_LENGTH FROM ALL_TAB_COLS
WHERE TABLE_NAME = upper(v_TableName) and upper(owner)=upper(v_ShadowUser) order by COLUMN_ID;
BEGIN
-- generate the trigger name for target table
v_TriggerName := 'TRG_' || upper(v_TableName) || '_Track' ;
-- check v_count , determine whether shdow table exist if not handle it
select count(*) into v_count from all_tables where table_name = upper(v_TableName) and owner = upper(v_ShadowUser);
-- iterate the cursor. generating column names prefixing ':new.'
OPEN Cols;
FETCH Cols INTO v_ColName,v_ColSize;
WHILE Cols%FOUND
LOOP
BEGIN
IF v_ColList IS NULL THEN
v_ColList := ':new.'||v_ColName ;
ELSE
v_ColList := v_ColList || ',' || ':new.'||v_ColName;
END IF;
FETCH Cols INTO v_ColName,v_ColSize;
END;
END LOOP;
CLOSE Cols;
-- iterate the cursor. get the shadow table columns
OPEN Shadow_Cols;
FETCH Shadow_Cols INTO v_ColName,v_ColSize;
WHILE Shadow_Cols%FOUND
LOOP
BEGIN
IF v_ColList_shadow IS NULL THEN
v_ColList_shadow := v_ColName;
ELSE
v_ColList_shadow := v_ColList_shadow || ',' || v_ColName;
END IF;
FETCH Shadow_Cols INTO v_ColName,v_ColSize;
END;
END LOOP;
CLOSE Shadow_Cols;
-- create trigger command. This will generate the trigger that dupilicates target table's data into shdow table
v_SQLCommand := 'CREATE or REPLACE TRIGGER '||v_TriggerName||'
AFTER INSERT OR UPDATE OR DELETE ON '||upper(v_TableName)||'
REFERENCING OLD AS old NEW AS new
FOR EACH ROW
DECLARE
ErrorCode NUMBER(19,0);
BEGIN
-- v_ColList_shadow : shdow table column list
-- v_ColList : target table column list with :new prefixed
INSERT INTO '|| v_ShadowUser ||'.'||upper(v_TableName)||'('||v_ColList_shadow||') values ('||v_ColList||');
EXCEPTION
WHEN OTHERS THEN ErrorCode := SQLCODE;
END;';
EXECUTE IMMEDIATE v_SQLCommand;
END;
END;
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;