Use string concatenation in UPDATEXML() - oracle

I want to write an update script for my oracle db. I want to replace old XML node values with new ones. But I need to do this for many values so I wanna outsource the logic in a procedure.
This is what I've come up with.
CREATE OR REPLACE PROCEDURE MY_PROCEDURE(
old_name IN NVARCHAR2,
new_name IN NVARCHAR2
)
AS
BEGIN
UPDATE
MY_TABLE
SET
ENTITY = UPDATEXML(ENTITY, '/*/Section', '<Section>' || new_name || '</Section>', 'xmlns="http://foo.bar.com/baz"')
WHERE
EXTRACTVALUE(ENTITY, '/*/Section', 'xmlns="http://foo.bar.com/baz"') = old_name;
END;
This is actually working really well - when I don't use string concatenation in UPDATEXML(). When I start using concatenation the execution of the procedure stops working / starts to throw exceptions.
I've also tried CONCAT(CONCAT('<Section>', new_name), '</Section>') instead of the ||-Operator. Didn't work out either.
The exception I get running the procedure in Oracle SQL Developer
ORA-06550: line 2, column 5:
PLS-00905: object MY_DB.MY_PROCEDURE is invalid
ORA-06550: line 2, column 5:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:

Related

Copy table from another schema doesn't work dynamically

I'm trying to copy tables (schema+data) from one schema to another by using:
create table as select * from my_table
I want to do it from a certain table list, so I wrote a cursor
DECLARE
p_site nvarchar2(200);
v_cmd nvarchar2(1000);
v_tablename nvarchar2(100);
CURSOR export_running IS
SELECT tablename FROM TABLES_TABLE;
BEGIN
p_site:='site_name';
OPEN export_running;
LOOP
FETCH export_running INTO v_tablename;
EXIT WHEN export_running%NOTFOUND;
v_cmd:='create table '||v_tablename||' as select * from '||p_site||'.'||v_tablename;
execute immediate v_cmd;
END LOOP;
CLOSE export_running;
END;
When I run the code I get
PLS-00382: expression is of wrong type
ORA-06550: line 20, column 6:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
But if I print the statement and run it, it works well.
Datapump is not an option here.
I'm doing it on SQL Developer, Oracle version 12.1.
I have full privileges on both schemas.
Is it a known issue that I cannot dynamically create a table from one schema to the other?
Any suggestion?
Thanks!
Don't use NVARCHAR2 data type. From the documentation:
execute_immediate_statement
dynamic_sql_stmt
String literal, string variable, or string expression that represents
a SQL statement. Its type must be either CHAR, VARCHAR2, or CLOB.

Create Table in a Loop inside Stored Procedure Oracle SQL

I am attempting to create a Oracle stored procedure which creates partitioned tables based off of a table containing the table names and the column to be partitioned with. A separate PL/SQL block iterates through the table and calls the procedure with the table name and the column name.
Procedure:
create or replace PROCEDURE exec_multiple_table_create (
table_name IN VARCHAR2,
column_name IN VARCHAR2
) IS
stmt VARCHAR2(5000);
tablename VARCHAR2(50);
columnname VARCHAR2(50);
BEGIN
tablename := table_name;
columnname := column_name;
-- DBMS_OUTPUT.PUT_LINE(tablename);
-- DBMS_OUTPUT.PUT_LINE(columnname);
stmt := 'create table '
|| TABLENAME
|| '_temp as (select * from '
|| COLUMNNAME
|| ' where 1=2)';
EXECUTE IMMEDIATE stmt
USING IN table_name, column_name;
stmt := 'alter table '
|| tablename
|| '_temp modify partition by range('
|| columnname
|| ')
(PARTITION observations_past VALUES LESS THAN (TO_DATE(''20000101'',''YYYYMMDD'')),
PARTITION observations_CY_2000 VALUES LESS THAN (TO_DATE(''20010101'',''YYYYMMDD'')),
PARTITION observations_CY_2001 VALUES LESS THAN (TO_DATE(''20020101'',''YYYYMMDD'')),
PARTITION observations_CY_2002 VALUES LESS THAN (TO_DATE(''20030101'',''YYYYMMDD'')),
PARTITION observations_CY_2003 VALUES LESS THAN (TO_DATE(''20040101'',''YYYYMMDD'')),
PARTITION observations_CY_2004 VALUES LESS THAN (TO_DATE(''20050101'',''YYYYMMDD'')),
PARTITION observations_CY_2005 VALUES LESS THAN (TO_DATE(''20060101'',''YYYYMMDD'')),
PARTITION observations_CY_2006 VALUES LESS THAN (TO_DATE(''20070101'',''YYYYMMDD'')),
PARTITION observations_CY_2007 VALUES LESS THAN (TO_DATE(''20080101'',''YYYYMMDD'')),
PARTITION observations_CY_2008 VALUES LESS THAN (TO_DATE(''20090101'',''YYYYMMDD'')),
PARTITION observations_CY_2009 VALUES LESS THAN (TO_DATE(''20100101'',''YYYYMMDD'')),
PARTITION observations_CY_2010 VALUES LESS THAN (TO_DATE(''20110101'',''YYYYMMDD'')),
PARTITION observations_FUTURE VALUES LESS THAN ( MAXVALUE ) )';
EXECUTE IMMEDIATE stmt
USING IN table_name, column_name;
RETURN;
END exec_multiple_table_create;
The PL/SQL block which is using the stored proc is:
BEGIN
FOR partition_item IN (
SELECT
table_name,
partition_column
FROM
partition_table
) LOOP
exec_multiple_table_create(partition_item.table_name, partition_item.partition_column);
END LOOP;
END;
Now, when I try executing the thing, this is what I am seeing:
Error report -
ORA-06550: line 9, column 9:
PLS-00905: object SCG_MYACCT_CUSTOMPC.EXEC_MULTIPLE_TABLE_CREATE is invalid
ORA-06550: line 9, column 9:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I have a feeling that I am missing something. Please let me know what it is. The table containing the reference data exists and contains data.
I have tried refreshing the table, rewriting and modifying the pl/sql block & the procedure code. Nothing seems to be working.
Thanks in advance.
UPDATE 1:
There was a glitch in the stored procedure where I needed to refer to the tablename rather than the columnname in the code above. However, I am getting a different error right now.
Error report -
ORA-06546: DDL statement is executed in an illegal context
ORA-06512: at "SCG_MYACCT_CUSTOMPC.EXEC_MULTIPLE_TABLE_CREATE", line 18
ORA-06512: at line 9
ORA-06512: at line 9
06546. 00000 - "DDL statement is executed in an illegal context"
*Cause: DDL statement is executed dynamically in illegal PL/SQL context.
- Dynamic OPEN cursor for a DDL in PL/SQL
- Bind variable's used in USING clause to EXECUTE IMMEDIATE a DDL
- Define variable's used in INTO clause to EXECUTE IMMEDIATE a DDL
*Action: Use EXECUTE IMMEDIATE without USING and INTO clauses to execute
the DDL statement.
Please help me out with this as well.
UPDATE 2:
I removed the USING part of the EXECUTE IMMEDIATE statement. That seemed to take care of the error I posted. Getting a different error with versions now:
Error starting at line : 1 in command -
BEGIN
FOR partition_item IN (
SELECT
table_name,
partition_column
FROM
partition_table
) LOOP
exec_multiple_table_create(partition_item.table_name, partition_item.partition_column);
END LOOP;
END;
Error report -
ORA-00406: COMPATIBLE parameter needs to be 12.2.0.0.0 or greater
ORA-00722: Feature "Conversion into partitioned table"
ORA-06512: at "SCG_MYACCT_CUSTOMPC.EXEC_MULTIPLE_TABLE_CREATE", line 37
ORA-06512: at line 9
ORA-06512: at line 9
00406. 00000 - "COMPATIBLE parameter needs to be %s or greater"
*Cause: The COMPATIBLE initialization parameter is not high
enough to allow the operation. Allowing the command would make
the database incompatible with the release specified by the
current COMPATIBLE parameter.
*Action: Shutdown and startup with a higher compatibility setting.

Getting error while forming Dynamic PLSQL

I am trying to form below SQL statement. but getting this weird error which I can't seem to figure out. I have executed each statement individually outside the loop and they are working fine. Please someone help me finding the error.
Error
ORA-06550: line 6, column 14:
PLS-00306: wrong number or types of arguments in call to '||'
ORA-06550: line 6, column 7:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
PLSQL Statement:
DECLARE
L_sql VARCHAR2(2000):=NULL;
BEGIN
FOR val IN (SELECT generation_qtr from test_1)
LOOP
L_sql:=L_sql ||' MAX(DECODE(generation_qtr, '||''''||val||''''||' cum_actual_gen)) AS ' || val ||','||chr(10);
END LOOP;
L_sql:='SELECT assetname, '|| L_sql;
L_sql:=substr(L_sql,1,LENGTH(L_sql)-1);
dbms_output.put_line(L_sql);
END;
Oracle Version -11.2
You're referring to val directly, but that's a cursor row type. You need to specify the column name, even if there is only one:
... ||val.generation_qtr|| ...
... in both places you use it in the concatenation. So it woudl become:
L_sql:=L_sql ||' MAX(DECODE(generation_qtr, '||''''
|| val.generation_qtr ||''''||' cum_actual_gen)) AS '
|| val.generation_qtr ||','||chr(10);
If you're going to execute this dynamically then the new line characters aren't going to be very useful, but I guess they help for debugging.

Create user through procedure on another database via database link

I have created user on another database using database link and stored procedure but facing problem while grant permission to the new created users.
Check below code:
CREATE OR replace PROCEDURE Hostname10 (user_name IN VARCHAR2,
pass_word IN VARCHAR2,
table_space IN VARCHAR2,
pro_file IN VARCHAR2)
AS
BEGIN
dbms_utility.Exec_ddl_statement#rahul2('CREATE USER '
||user_name
||' IDENTIFIED BY '
||pass_word
||' DEFAULT TABLESPACE '
||table_space
|| ' PROFILE '
|| pro_file
|| ' ACCOUNT UNLOCK');
dbms_utility.Exec_ddl_statement#rahul2('grant create table,create session,create view,create sequence,create procedure,create job,create synonym to'
||user_name
||'');
END;
/
I am getting error while executing it:
Error:
Error report -
ORA-06550: line 1, column 7:
PLS-00201: identifier 'HOSTANAME10' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Your error message says:
PLS-00201: identifier 'HOSTANAME10' must be declared
^
But your procedure is created as Hostname10. So this is just a typo, you have an extra a in the name when you try to call the procedure.
You also seem to have a mistake in the grant call, though you're not currently getting that far; that ends with:
... create synonym to'
||user_name
||'');
so in the generated command there will be no space between to and the username; that needs to be:
... create synonym to '
||user_name);
Concatenating the null/empty string after the username isn't doing anything so I've taken the liberty of removing that too.

Why do I get an error as I try to call a procedure?

I created a procedure named greet as :
create procedure greet(message in char(50))
as
begin
dbms_output.put_line('Greet Message : ' || message);
end;
The procedure compiled successfully but when I try to call it as :
execute greet('Hey ! This is a self created procedure :)');
I get an error :
execute greet('Hey ! This is a self created procedure :)')
Error report:
ORA-06550: line 1, column 7:
PLS-00905: object SUHAIL.GREET is invalid
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
What error is it ? Why do I get it ?
Note : 'suhail' is name of the current user connected to oracle server
I don't believe that your procedure compiled successfully. When I try to compile it on my system, I get syntax errors
SQL> create procedure greet(message in char(50))
2 as
3 begin
4 dbms_output.put_line('Greet Message : ' || message);
5 end;
6 /
Warning: Procedure created with compilation errors.
SQL> sho err
Errors for PROCEDURE GREET:
LINE/COL ERROR
-------- -----------------------------------------------------------------
1/32 PLS-00103: Encountered the symbol "(" when expecting one of the
following:
:= ) , default varying character large
The symbol ":=" was substituted for "(" to continue.
If I resolve the syntax errors (you cannot specify a length for an input parameter), it works
SQL> ed
Wrote file afiedt.buf
1 create or replace procedure greet(message in char)
2 as
3 begin
4 dbms_output.put_line('Greet Message : ' || message);
5* end;
SQL> /
Procedure created.
SQL> set serveroutput on;
SQL> execute greet('Hey ! This is a self created procedure :)');
Greet Message : Hey ! This is a self created procedure :)
PL/SQL procedure successfully completed.
I would be shocked if you really wanted the input parameter to be declared as CHAR. Almost always, you should use VARCHAR2 for character strings. It is exceptionally rare to come across a case where you really want the blank-padding semantics of a CHAR.
this is working dude;
create or replace
procedure greet(message in char)
as
begin
dbms_output.put_line('Greet Message : ' || message);
end;
see main property of char datatype is is the length of input data is less than the size you specified it'll add blank spaces.this case is not happened for varchar2.
in procedure above mentioned char property is violated so it's almost treat like varchar2. so if you remove size of input parameter it will work and also char support maximum length of input.

Resources