DDL doesn't work as expect inside Oracle block - oracle

I have a question about running ddl script in oracle block.
Basically I want to something for data migration. If a column has its data type changed, I want to create a temp column first, copy source column data to temp column, then drop source column, rename temp column to source, to finish migration process. But when I run the script below, it will only create a new temp column and copy data to the new column, but it will never run the last two alter table command. Can anyone figure out what the problem is? Thanks.
create table t (a int);
insert into t values(10);
--change the data type from integer to varchar2(10);
begin
execute immediate 'alter table t modify a varchar2(10)';
exception when others then
begin
execute immediate 'alter table t add b varchar2(10)';
execute immediate 'update t set b = a';
execute immediate 'alter tabe t drop column a';
execute immediate 'alter table t rename column b to a';
exception when others then
dbms_output.put_line('cann''t change column a from integer to varchar2(10) due to error:' || sqlerrm);
end;
end;
/

You do see the drop column alter command has the typo "alter tabe...", right? If this is the code you're trying to run, as soon as it hits this statement you should see your error message, and your procedure only does the first two execute statements.

Related

How to use this execute immediate create table to insert values in table

I am using execute immediate to create table and use this table into my code to insert some values when i run this code .it gives me error that table and view does not exist.why?. this code has ability to create table because when i use drop and create table command with existing table by using execute immediate then my code works fine and when i just remove drop table syntax, my code does not work, Please help me to clear my concept about dynamic SQL-----------
SET serveroutput ON
DECLARE
ddl_table VARCHAR2(200);
r_emp SYS.ODCINUMBERLIST := SYS.ODCINUMBERLIST();
v_array SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST('ACCT_ID',
'PARENT_ACCT_ID',
'CUST_ID',
'ACCT_NAME',
'BILLING_CYCLE_TYPE',
'PAID_FLAG',
'BILL_DELIVER_METHOD');
BEGIN
ddl_table := 'CREATE TABLE test123(
v_column VARCHAR2(50),
v_count NUMBER
)';
EXECUTE IMMEDIATE ddl_table;
DBMS_OUTPUT.ENABLE;
FOR i IN 1 .. v_array.COUNT LOOP
r_emp.EXTEND;
EXECUTE IMMEDIATE 'SELECT COUNT(*)
FROM account_profile
WHERE NOT REGEXP_LIKE(' ||v_array(i) || ',''[A-Za-z0-9.]'')'
INTO r_emp(i);
IF r_emp(i) <> 0 THEN
DBMS_OUTPUT.PUT_LINE(v_array(i) || ': ' || r_emp(i));
INSERT INTO test123 VALUES (v_array(i), r_emp(i));
END IF;
END LOOP;
END;
/
Error report -
ORA-06550: line 24, column 17:
PL/SQL: ORA-00942: table or view does not exist
Your problem is that the annonymous block is pre-validated (pre-compiled) before it is valid. Oracle will check all objects in use before executing it. Since you are creating test123 dynamically it doesn't exist so your insert statement fails.
You can instead, use an EXECUTE IMMEDIATE command to also insert the data on your test123 table.
The way you use the EXECUTE IMMEDIATE for an insert command is either concatenating the parameters or preparing them, I prefer the later. Like this
execute immediate 'insert into test123 values (:param1,:param2)' using v_array(i), r_emp(i);
Here is the official documentation for the EXECUTE IMMEDIATE command.
Though it shows how it works and explain the usage of it, it doesn't particularly answer you direct question on the comments.
So
can you explain :param1,:param2
Those are called "binding" parameters that will be replaced by the variables used after the using statement. It doesn't matter their name only the order in which they appear on the string. The number of parameters within the dynamic string needs to match the number of parameters after the using statement.
why we use it with : colon and what are these
The colon : there is just to make it easier for the parser to know what to replace and where when using the variables you provided after the using statement
Translating that execute immediate it would be something like this:
... values (:param1,:param2)' using v_array(i), r_emp(i)
Hey "execute immediate" whenever you see :param1 please replace it with the content of the variable I'm providing as v_array(i) and whenever you see :param2 please replace it with the content of the variable I'm providing as r_emp(i)

How to rename multiple columns in oracle using one Alter table statement?

The only thing I found is renaming one column at a time:
ALTER TABLE table_name
RENAME COLUMN old_name TO new_name;
I read Oracle documentations, and couldn't get the answer for many columns at a time .
Ref: https://docs.oracle.com/javadb/10.6.2.1/ref/rrefsqljrenamecolumnstatement.html
It is not possible to rename multiple table columns in a single command, as of Oracle 18c.
The Oracle 18c SQL Language Reference includes the below diagram to illustrate how the RENAME_COLUMN_CLAUSE of the ALTER TABLE command works. Unfortunately, almost every column property can be modified in groups, except for renaming.
You can use user_tab_columns dictionary view as a data source within a cursor for a loop statement
declare
v_table_name varchar2(40):='mytable';
begin
for c in ( select from user_tab_columns where table_name = upper(v_table_name) )
loop
execute immediate ('ALTER TABLE '||c.table_name||' RENAME COLUMN '||c.column_name
||' TO new_'||c.column_name);
end loop;
end;

Partitioned Index rebuild in Pl-sql block

I want to rebuild/unusable a partitioned index in pl-sql block. I have written in below code in pl-sql block,
declare m_ErrorMsg varchar2(100);
begin
execute immediate
'Alter Index TCMS.TTC_PERF_IDX01 Rebuild Partition
TMP_TRN_CUSTOMER_0;';
EXCEPTION WHEN OTHERS THEN
m_ErrorMsg := SUBSTR(SQLERRM(SQLCODE), 1, 200) ;
dbms_output.put_line(m_ErrorMsg);
end;
Above code is giving exception: 'ORA-14010: this physical attribute may not be specified for an index partition'.
If I execute below command on Command Promt then it's working,
Alter Index TCMS.TTC_PERF_IDX01 Rebuild Partition TMP_TRN_CUSTOMER_0;
Please suggest me correct way to write it in plsql bloc.
Clearly you cannot use ; with execute immediate clause.
your statment should be:
execute immediate 'Alter Index TCMS.TTC_PERF_IDX01 Rebuild Partition TMP_TRN_CUSTOMER_0';

Oracle DB - identifier too long

I have created a connection in SQL Developer and added several tables to database. Also, I have defined some triggers. They all work well except for one that gives "identifier too long" error. I am aware of 30 character limit, but i can't see what causes this particular error. With this code, I'm trying to implement the Short-circuit keys.
Tables:
Izvestaj (IzvestajID, Datum, Opis, Tekst, PredmerID, NarucilacID, OsobaID, IzvrsilacID)
Predmer (PredmerID, Datum, Naziv, IzvrsilacID, LokacijaID)
Izvrsilac (IzvrsilacID, Naziv)
Italic values represent the primary keys of relations (tables).
Triggers:
create or replace TRIGGER "T_IZM_IZV"
AFTER UPDATE OF IZVRSILACID ON PREDMER
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
EXECUTE IMMEDIATE ('ALTER TRIGGER'||'T_ZABRANA_IZM_IZV'||'DISABLE');
UPDATE IZVESTAJ SET IZVRSILACID=:NEW.IZVRSILACID
WHERE PREDMERID=:NEW.PREDMERID;
EXECUTE IMMEDIATE ('ALTER TRIGGER'||'T_ZABRANA_IZM_IZV'||'ENABLE');
END;
create or replace TRIGGER "T_ZABRANA_IZM_IZV"
BEFORE UPDATE OF IzvrsilacID ON Izvestaj
FOR EACH ROW
BEGIN
RAISE_APPLICATION_ERROR(-20000, 'Direct altering of IzvrsilacID is forbidden');
END;
I have added some rows to table "Predmer" (Izvrsilac and Izvestaj also have some values inserted) and when I try to change value of column "IzvrsilacID" (foreign key), i get an error:
UPDATE "KORISNIK"."PREDMER" SET IZVRSILACID = '1' WHERE ROWID = 'AAAFBRAABAAALDxAAB' AND ORA_ROWSCN = '675526'
ORA-00972: identifier is too long
ORA-06512: at "KORISNIK.T_IZM_IZV", line 4
ORA-04088: error during execution of trigger 'KORISNIK.T_IZM_IZV'
One error saving changes to table "KORISNIK"."PREDMER":
Row 2: ORA-00972: identifier is too long
ORA-06512: at "KORISNIK.T_IZM_IZV", line 4
ORA-04088: error during execution of trigger 'KORISNIK.T_IZM_IZV'
What could cause this error? I've tried several things, even renaming triggers, tables and columns to one-letter names, but without any success.
P.S. Sorry about naming. I didn't want to translate table and column names to English because it would differ their length, which seems to cause an error in the first place.
These lines:
EXECUTE IMMEDIATE ('ALTER TRIGGER'||'T_ZABRANA_IZM_IZV'||'DISABLE');
EXECUTE IMMEDIATE ('ALTER TRIGGER'||'T_ZABRANA_IZM_IZV'||'ENABLE');
Should look like this:
EXECUTE IMMEDIATE ('ALTER TRIGGER '||'T_ZABRANA_IZM_IZV'||' DISABLE');
EXECUTE IMMEDIATE ('ALTER TRIGGER '||'T_ZABRANA_IZM_IZV'||' ENABLE');
Note the added spaces.
Without the spaces added, you were trying execute:
ALTER TRIGGERT_ZABRANA_IZM_IZVDISABLE
and
ALTER TRIGGERT_ZABRANA_IZM_IZVENABLE
which clearly isn't going to work.
Hope that helps.

DDL statements in PL/SQL?

I am trying the code below to create a table in PL/SQL:
DECLARE
V_NAME VARCHAR2(20);
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE TEMP(NAME VARCHAR(20))';
EXECUTE IMMEDIATE 'INSERT INTO TEMP VALUES(''XYZ'')';
SELECT NAME INTO V_NAME FROM TEMP;
END;
/
The SELECT statement fails with this error:
PL/SQL: ORA-00942: table or view does not exist
Is it possible to CREATE, INSERT and SELECT all in a single PL/SQL Block one after other?
I assume you're doing something like the following:
declare
v_temp varchar2(20);
begin
execute immediate 'create table temp(name varchar(20))';
execute immediate 'insert into temp values(''XYZ'')';
select name into v_name from temp;
end;
At compile time the table, TEMP, does not exist. It hasn't been created yet. As it doesn't exist you can't select from it; you therefore also have to do the SELECT dynamically. There isn't actually any need to do a SELECT in this particular situation though you can use the returning into syntax.
declare
v_temp varchar2(20)
begin
execute immediate 'create table temp(name varchar2(20))';
execute immediate 'insert into temp
values(''XYZ'')
returning name into :1'
returning into v_temp;
end;
However, needing to dynamically create tables is normally an indication of a badly designed schema. It shouldn't really be necessary.
I can recommend René Nyffenegger's post "Why is dynamic SQL bad?" for reasons why you should avoid dynamic SQL, if at all possible, from a performance standpoint. Please also be aware that you are much more open to SQL injection and should use bind variables and DBMS_ASSERT to help guard against it.
If you run the program multiple time you will get an error even after modifying the program to run the select statement as dynamic SQL or using a returning into clause.
Because when you run the program first time it will create the table without any issue but when you run it next time as the table already created first time and you don't have a drop statement it will cause an error: "Table already exists in the Database".
So my suggestion is before creating a table in a pl/sql program always check if there is any table with the same name already exists in the database or not. You can do this check using a Data dictionary views /system tables which store the metadata depending on your database type.
For Example in Oracle you can use following views to decide if a tables needs to be created or not:
DBA_TABLES ,
ALL_TABLES,
USER_TABLES

Resources