I'm trying to execute two DML statements in DB2 using JDBC and I keep getting a syntax error:
ALTER TABLE "TEST" ALTER COLUMN "COL1" SET DATA TYPE INT;
ALTER TABLE "TEST" ALTER COLUMN "COL1" SET NOT NULL;
If I execute them one by one everything is OK.
I've tried with no semicolons, only one semicolon on the first statement. I also tried with several \r\n and I really can't understand what the problem is.
The exception is the following:
Caused by: com.ibm.db2.jcc.am.SqlSyntaxErrorException: DB2 SQL Error: SQLCODE=-104, SQLSTATE=42601, SQLERRMC=;
ALTER TABLE "TEST";1" SET DATA TYPE INT;<space>, DRIVER=3.65.77
Does anyone know what might be the problem?
Obviously, the execute() method accepts only a single statement, not multiple statements delimited by whatever you might try. It's clearly stated in the API documentation: "Executes the given SQL statement".
You can wrap multiple statements into a (single) compound statement:
begin execute immediate 'alter ...'; execute immediate 'alter ...'; end
Related
I am trying to execute an SQL command within dynamic SQL with bind variables:
-- this procedure is a part of PL/SQL package Test_Pkg
PROCEDURE Set_Nls_Calendar(calendar_ IN VARCHAR2)
IS
BEGIN
EXECUTE IMMEDIATE
'ALTER SESSION
SET NLS_CALENDAR = :cal'
USING IN calendar_;
END Set_Nls_Calendar;
Then on the client side, I am trying to invoke the procedure:
Test_Pkg.Set_Nls_Calendar('Thai Buddha');
But this get's me ORA-02248: invalid option for ALTER SESSION.
And my question is: Why cannot I use bind variables in DDL/SCL statements in dynamic SQL?
Bind variables are not allowed in DDL statements. So following statements will cause errors:
Example #1: DDL statement. Will cause ORA-01027: bind variables not allowed for data definition operations
EXECUTE IMMEDIATE
'CREATE TABLE dummy_table ( dummy_column NUMBER DEFAULT :def_val )'
USING 42;
Example #2: DDL statement. Will cause ORA-00904: : invalid identifier
EXECUTE IMMEDIATE
'CREATE TABLE dummy_table ( :col_name NUMBER )'
USING var_col_name;
Example #3: SCL statement. Will cause ORA-02248: invalid option for ALTER SESSION
EXECUTE IMMEDIATE
'ALTER SESSION SET NLS_CALENDAR = :cal'
USING var_calendar_option;
Problem
To understand why this happens, we need to look at How Dynamic SQL Statements Are Processed.
Typically, an application program prompts the user for the text of a SQL statement and the values of host variables used in the statement. Then Oracle parses the SQL statement. That is, Oracle examines the SQL statement to make sure it follows syntax rules and refers to valid database objects. Parsing also involves checking database access rights1, reserving needed resources, and finding the optimal access path.
1 Emphasis added by answerer
Note that parsing step happens before binding any variables to the dynamic statement. If you examine the above four examples, you will realize that there is no way for the parser to guarantee the syntactical validity of these dynamic SQL statements without knowing the values for bind variables.
Example #1: Parser cannot tell if the bind value will be valid. What if instead of USING 42, programmer wrote USING 'forty-two'?
Example #2: Parser cannot tell if :col_name would be a valid column name. What if the bound column name was 'identifier_that_well_exceeds_thirty_character_identifier_limit'?
Example #3: Values for NLS_CALENDAR are built in constants (for a given Oracle version?). Parser cannot tell if the bound variable will have a valid value.
So the answer is that you cannot bind schema elements such as table names, column names in dynamic SQL. Nor you can bind built in constants.
Solution
The only way to achieve referencing schema elements/constants dynamically is to use string concatenation in dynamic SQL statements.
Example #1:
EXECUTE IMMEDIATE
'CREATE TABLE dummy_table ( dummy_column NUMBER DEFAULT ' || to_char(42) || ')';
Example #2:
EXECUTE IMMEDIATE
'CREATE TABLE dummy_table (' || var_col_name || ' NUMBER )';
Example #3:
EXECUTE IMMEDIATE
'ALTER SESSION SET NLS_CALENDAR = ''' || var_calendar_option || '''';
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.
What is the significance of execute immediate in PL/SQL when used with DML and DDL statements? Does execute immediate implicitly commit to a database like DDL statements do ?
The following is an example I have encountered:
SELECT 'TRUNCATE TABLE BD_BIDS_APPR_DET' INTO V_SQL1 FROM DUAL;
EXECUTE IMMEDIATE V_SQL1;
SELECT 'TRUNCATE TABLE BD_BIDS_SYS_DET' INTO V_SQL1 FROM DUAL;
EXECUTE IMMEDIATE V_SQL1;
SELECT 'TRUNCATE TABLE BD_BIDS_EXT_DET' INTO V_SQL1 FROM DUAL;
EXECUTE IMMEDIATE V_SQL1;
It is for running native dynamic SQL.
For DML you'd use it when running statements that you don't have available at compile time, e.g. if the column list is based on a selection from the user.
In your case it's being used because DDL cannot be run as static SQL from within PL/SQL. Only certain query, DML and TCL commands are valid. Anything else has to be treated as dynamic.
I'd say it's rare to need to use DDL from a PL/SQL block. TRUNCATE might be reasonable; if you find anything creating or dropping objects on the fly then that might be more of a concern as it can suggest a suboptimal data model.
EXECUTE IMMEDIATE itself does not automatically commit; but if you execute DDL then that will behave the same as if you ran it outside PL/SQL, so it will commit in your case, yes.
Incidentally, I'm not sure why your code is using an intermediate variable to hold the statement; that's useful if you want to display what it's going to run maybe, but you don't seem to be doing that. What you have could be done as:
EXECUTE IMMEDIATE 'TRUNCATE TABLE BD_BIDS_EXT_DET';
i.e. without using V_SQL_1 at all.
It provides end-to-end support when executing a dynamic SQL statement or an anonymous PL/SQL block. It substitutes the unknown values, executes the statement, returns the data, and then releases the resources.
EXECUTE IMMEDIATE [dynamic SQL string statement without terminator]
[INTO {define_variable [, define_variable] ... | record}]
[USING [IN|OUT|IN OUT] bind_argument [, [IN|OUT|IN OUT] bind_arguments] ];
define_variable is a PL/SQL variable or record that captures the result set of the SELECT query
Bind arguments are the actual values or local variables which would replace the bind variables in the SQL string statement.
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
When I run the following in an Oracle shell it works fine
truncate table table_name
But when I try to put it in a stored procedure
CREATE OR REPLACE PROCEDURE test IS
BEGIN
truncate table table_name;
END test;
/
it fails with
ERROR line 3, col 14, ending_line 3, ending_col 18, Found 'table', Expecting: # ROW or ( or . or ; :=
Why?
All DDL statements in Oracle PL/SQL should use Execute Immediate before the statement. Hence you should use:
execute immediate 'truncate table schema.tablename';
As well as execute immediate you can also use
DBMS_UTILITY.EXEC_DDL_STATEMENT('TRUNCATE TABLE tablename;');
The statement fails because the stored proc is executing DDL and some instances of DDL could invalidate the stored proc. By using the execute immediate or exec_ddl approaches the DDL is implemented through unparsed code.
When doing this you neeed to look out for the fact that DDL issues an implicit commit both before and after execution.
try the below code
execute immediate 'truncate table tablename' ;
You should know that it is not possible to directly run a DDL statement like you do for DML from a PL/SQL block because PL/SQL does not support late binding directly it only support compile time binding which is fine for DML. hence to overcome this type of problem oracle has provided a dynamic SQL approach which can be used to execute the DDL statements.The dynamic sql approach is about parsing and binding of sql string at the runtime.
Also you should rememder that DDL statements are by default auto commit hence you should be careful about any of the DDL statement using the dynamic SQL approach incase if you have some DML (which needs to be commited explicitly using TCL) before executing the DDL in the stored proc/function.
You can use any of the following dynamic sql approach to execute a DDL statement from a pl/sql block.
1) Execute immediate
2) DBMS_SQL package
3) DBMS_UTILITY.EXEC_DDL_STATEMENT (parse_string IN VARCHAR2);
Hope this answers your question with explanation.