ORACLE Batching DDL statements within a Execute Immediate - sql

I'm trying to run multiple ddl statements within one Execute Immediate statement.
i thought this would be pretty straight forward but it seems i'm mistaken.
The idea is this:
declare v_cnt number;
begin
select count(*) into v_cnt from all_tables where table_name='TABLE1' and owner = 'AMS';
if v_cnt = 0 then
execute immediate 'CREATE TABLE TABLE1(VALUE VARCHAR2(50) NOT NULL) ALTER TABLE TABLE1 ADD (MYVAL2 NVARCHAR2(10))';
end if;
end;
however this results in an error
ORA-00911: invalid character
ORA-06512: at line 10
Each of the statements within the batch run fine if i execute them by themselves. and if i take this statement and execute it, it will run fine (with the ; between the 2 statements). If i remove the ; between statements i get a different error about invalid option
the plan is that i'll be able to build a table, export the table schema for this table including all it's alter statements, and then run the batch against another system as part of an install/update process.
So, how do i batch these DDL statements within a single execute immediate? Or is there a better way to do what i'm needing?
I'm a bit of a Oracle newb, i must admit. Thank you all for your patience.

The semicolon is not part of Oracle's SQL syntax. SQL*Plus and other client side tools use semicolon to signal the end of a SQL Statement, but the server doesn't see it.
We can force SQL*Plus to pass the semicolon to the DB:
SQL> set sqlterminator off
SQL> select * from user_tables;
2 /
select * from user_tables;
*
ERROR at line 1:
ORA-00911: invalid character
If i take this statement and execute it, it will run fine (with the ; between the 2 statements) The client tool you are using, breaks it into two calls to the DB.
So, I don't think it is possible to pass multiple statements inside an execute immediate.
I suppose one could call execute immediate with a string containing an anonymous PL/SQL block, with individual calls to execute immediate inside it ... and I don't know what the point of doing that would be. ;)

Why do you need a single EXECUTE IMMEDIATE call? Surely just do it as 2 calls?
Bear in mind that each DDL statement contains an implicit COMMIT, so there's no concurency benefit to doing it as a single call.
Also, why not just set up the table correctly in the first call? You could do...
CREATE TABLE TABLE1(VALUE VARCHAR2(50) NOT NULL, MYVAL2 NVARCHAR2(10))
...instead of needing 2 calls.
Also, have you looked at DBMS_METADATA... it can generate DDL for objects such as tables for you.

Related

Unable to create DB2 Procedure through command line

I am trying to create a procedure to do a blanket revoking of executeauth for procedures from a schema. This is in line with trying to secure a non-restricted database.
CREATE PROCEDURE PROC_REV
LANGUAGE SQL
MODIFIES SQL DATA
BEGIN
DECLARE v_NAME VARCHAR(400);
FOR v1 AS
c1 CURSOR FOR
select specificname from SYSCAT.ROUTINEAUTH where grantee='PUBLIC' and schema='SYSPROC' and routinetype='P'
DO
SET v_NAME = specificname;
SET v_GrantQuery = 'revoke execute on specific procedure '|| v_NAME ||' from PUBLIC';
EXECUTE IMMEDIATE v_GrantQuery;
END FOR;
END#
and this is the command I run to process the file with the code above.
db2 -td# -svf RoutineAuthRevoke.db2
However, I keep running into this error
SQL0553N An object cannot be created with the schema name "SYSFUN ". LINE NUMBER=1. SQLSTATE 42939
I'm fairly new to DB2 and this is my first foray into writing db2 procedure scripts. Would anyone be able to spot the "SYSFUN " because I sure as hell can't. The only other way I can revoke 310 entries from SYSPROC for PUBLIC is through a batch file and I figured, procedures might be a cleaner way of achieving this. I would really appreciate any help with either this error or with the code itself.
A number of problems.
You can't create a routine in the SYSFUN schema as the error message shows. You get this message because the statement VALUES CURRENT SCHEMA returns SYSFUN in your session. You must either run the SET SCHEMA SOME_VALID_SCHEMA_NAME statement before CREATE or use fully qualified routine name like SOME_VALID_SCHEMA_NAME.PROC_REV.
Variable v_GrantQuery is not defined in the routine.
According to the syntax of REVOKE (routine privileges) statement, you should generate the REVOKE statement using fully qualified routine name and with RESTRICT clause at the end. The easiest way to do it with a compound statement (you don't need to create a routine for that):
BEGIN
FOR v1 AS
select 'REVOKE EXECUTE ON SPECIFIC PROCEDURE "' || schema ||'"."'|| specificname || '" FROM PUBLIC RESTRICT' AS STMT
from SYSCAT.ROUTINEAUTH
where grantee='PUBLIC' and schema='SYSPROC' and routinetype='P'
DO
EXECUTE IMMEDIATE v1.STMT;
END FOR;
END#

ora-00900 invalid sql statement execute immediate

I am trying to solve a task with a dynamic SQL, but facing an issue ora-00900 invalid sql statement.
execute immediate 'alter table my_table set interval (NUMTOYMINTERVAL(1, ''MONTH''))';
However, it works in the anonymous block treating the statement to be executed as a string.
DECLARE
str VARCHAR2 (250) := 'alter table my_table set interval (NUMTOYMINTERVAL(1, ''MONTH''))';
BEGIN
execute immediate str;
END;
So where is the issue in the first case? It looks like with escape quotes, but can’t catch this.
As the error says, execute immediate is not a SQL statement. There is an execute command in some clients - SQL*Plus, SQL Developer, SQLcl and possibly others - which is a shorthand wrapper around an anonymous block; but from the error you don't seem to be using one of those. If you were you'd get ORA-06550 and PLS-0010: Encounter edthe symbol "alter table..." when expecting...
execute immediate is a PL/SQL statement. So it is only valid within a PL/SQL block.
It has no meaning in plain SQL; the only execute in the SQL reference is referring to directory object privileges.
Your alter table doesn't make much sense anyway, not sure if that's supposed to be an update, but if so it isn't say what to set to that interval value. It isn't obvious why you need it to be dynamic either. Possibly that reason and the actual statement have been lost in simplifying for posting your question.

oracle sql developer first time user

I am new to plsql and trying to use oracle sql developer, I try to run a simple procedure with dbms output line and i get the following error,
ora-00904
, the code is
create or replace PROCEDURE proc_101 IS
v_string_tx VARCHAR2(256) := 'Hello World';
BEGIN
dbms_output.put_line(v_string_tx);
END;
whether i click the run(green colour) or debug(red colour) i get the same error.
You can see from the above code, procedure doesn't access any objects but still i get the same error.
Your procedure is fine. You may not have permissions to be able to Create a Procedure. If this is the case test your procedure/code without actually Creating it in the Database first. For example, when I'm testing code in my Production database my oracle user cannot Create Procedures, Packages, Tables etc... And so I test my Procedures within my Own PL/SQL Blocks. When the code is good to go I can get a database administrator to Create the Procedures and/or Packages for me.
The below screenshot is code that simply tests the Procedure:
The below screenshot is code that does much more and tests the Procedure from within a PL/SQL Block
For more advanced situations this allows you to do so much more as you can create all sorts of Procedures/Functions and/or Cursors and test them immediately without needing to CREATE these objects in your Oracle Database.
I'd say that there's some other code in the worksheet which raises that error, not just the CREATE PROCEDURE you posted. For example, something like this SQL*Plus example (just to show what's going on - you'd get the same result in SQL Developer):
SQL> select pixie from dual;
select pixie from dual
*
ERROR at line 1:
ORA-00904: "PIXIE": invalid identifier
SQL>
SQL> create or replace PROCEDURE proc_101 IS
2 v_string_tx VARCHAR2(256) := 'Hello World';
3 BEGIN
4 dbms_output.put_line(v_string_tx);
5 END;
6 /
Procedure created.
SQL>
See? The first part raised ORA-00904 as there's no PIXIE column in DUAL, while the procedure is created correctly.
So - remove code which fails and everything should be OK.
Check with your DBA to make sure the dbms_output package has been installed on your database, and that you have permissions on it.

Run an oracle SQL script twice with different parameters

I have an SQL statement in Oracle SQL developer that has some variables:
DEFINE custom_date = "'22-JUL-2016'" --run1
DEFINE custom_date = "'25-JUL-2016'" --run2
SELECT * FROM TABLE WHERE date=&custom_date
The real query is much more complicated and has many more variables and new tables are created from the results of the query. How can I create a script so that the query is executed twice, the first time with the custom date set as in the first line and the second time as in the second line.
In Oracle, the &variable is a "substitution variable" and is not part of SQL; it is part of the SQL*Plus scripting language (understood by SQL Developer, Toad etc.)
The better option, and what you are asking about, is BIND VARIABLES. The notation is :variable (with a colon : instead of &), for example :custom_date.
The difference is that a substitution variable is replaced by its value in the front-end application (SQL Developer in your case) before the query is ever sent to the Oracle engine proper. A bind variable is substituted at runtime. This has several benefits; discussing them is outside the scope of your question.
When you execute a query with bind variables in SQL Developer, the program will open a window where you enter the desired values for the bind variables. You will have to experiment with that a little bit till you can make it work (for example I never remember if a date must be entered with the single quotes or without). Good luck!
Define is used in TRANSACT SQL. To do this Oracle way, You can create anonymus PL/SQL block, similar to this:
DECLARE
p_param1 DATE;
p_param2 NUMBER;
CURSOR c_cur1(cp_param1 DATE,cp_param2 NUMBER)
IS
SELECT * FROM table WHERE date = cp_param1
;
BEGIN
-- Execute it first time
p_param1 := TO_DATE('2016-09-01','YYYY-MM-DD');
FOR r IN c_cur1(p_param1)
LOOP
NULL;
END LOOP;
-- Execute it second time
p_param1 := TO_DATE('2015-10-11','YYYY-MM-DD');
FOR r IN c_cur1(p_param1)
LOOP
NULL;
END LOOP;
END;
And in it, You create cursor with parameters and execute it twice with different parameter.
I do not know why You want to execute this query twice, so the script abowe does nothing with results, but it certainly should execute Your query twice, with different params.

Dynamic SQL - Check syntax and semantics

With Oracle dynamic SQL one is able to execute a string containing a SQL statement. e.g.
l_stmt := 'select count(*) from tab1';
execute immediate l_stmt;
Is it possible to not execute l_stmt but check that the syntax and semantics is correct programmitically?
EXPLAIN PLAN will check the syntax and semantics of almost all types of SQL statements. And unlike DBMS_SQL.PARSE it will not implicitly execute anything.
The point of the explain plan is to show how Oracle will execute a statement. As a side-effect of generating the plan it must also check syntax, privileges, and generally do everything except actually run the statement. The explain plan itself is pointless and can be ignored, the statement is only run to check for any errors. As long as there are no errors, the statement is valid.
For example, the PL/SQL blocks below check the validity of a SELECT statement and a CREATE TABLE statement. They run without error so the syntax is fine.
begin
execute immediate 'explain plan for select * from dual';
end;
/
begin
execute immediate 'explain plan for create table just_some_table(a number)';
end;
/
Running a bad statement will generate an error. In at least this one test case, it generates the same error as if the statement was run by itself.
begin
execute immediate 'explain plan for select * from this_table_does_not_exist';
end;
/
ORA-00942: table or view does not exist
ORA-06512: at line 2
The syntax diagram in the manual implies it should run for all statements. However, there appear to be at least a few statement types that do not work, such as ALTER SESSION.
begin
execute immediate 'explain plan for alter session set optimizer_features_enable = ''11.2.0.4''';
end;
/
ORA-00900: invalid SQL statement
ORA-06512: at line 2
Slightly off-topic - are you trying to build a completely generic SQL interface, like a private SQL Fiddle built in PL/SQL? Do you need to worry about things like preventing users from attempting to run certain statement types, and ensuring there are no trailing semicolons? If so I can edit the question to help with some of those difficult dynamic SQL tasks.
I think that the only "solution" is to use DBMS_SQL.PARSE().
It is not perfect but it is the best that you can get
Hope this way you can check the query formed before execution.
set serveroutput on;
DECLARE
lv_sql VARCHAR2(1000);
lv_cnt PLS_INTEGER;
BEGIN
lv_sql:='select count(*) from tab1';
dbms_output.put_line(lv_sql);
--EXECUTE IMMEDIATE lv_sql INTO lv_cnt;
END