How to use SET OPTION within a DB2 stored procedure - sql

I read (and tried) that I cannot use WITH UR in DB2 stored procedures. I am told that I can use SET OPTION to achieve the same. However, when I implement it in my stored procedure, it fails to compile (I moved around its location same error). My questions are:
Can I really not use WITH UR after my SELECT statements within a procedure?
Why is my stored procedure failing to compile with the below error
message?
Here is a simplified version of my code:
CREATE OR REPLACE PROCEDURE MySchema.MySampleProcedure()
DYNAMIC RESULT SETS 1
LANGUAGE SQL
SET OPTION COMMIT=*CHG
BEGIN
DECLARE GLOBAL TEMPORARY TABLE TEMP_TABLE AS (
SELECT 'testValue' as "Col Name"
) WITH DATA
BEGIN
DECLARE exitCursor CURSOR WITH RETURN FOR
SELECT *
FROM SESSION.TEMP_TABLE;
OPEN exitCursor;
END;
END
#
Error Message:
SQL0104N An unexpected token "SET OPTION COMMIT=*CHG" was found
following " LANGUAGE SQL
Here is code/error when I use WITH UR
CREATE OR REPLACE PROCEDURE MySchema.MySampleProcedure()
LANGUAGE SQL
DYNAMIC RESULT SETS 1
--#SET TERMINATOR #
BEGIN
DECLARE GLOBAL TEMPORARY TABLE TEMP_TABLE AS (
SELECT UTI AS "Trade ID" FROM XYZ WITH UR
) WITH DATA;
BEGIN
DECLARE exitCursor CURSOR WITH RETURN FOR
SELECT *
FROM SESSION.TEMP_TABLE;
OPEN exitCursor;
END;
END
#
line 9 is where the DECLARE GLOBAL TEMPORARY ... is
DB21034E The command was processed as an SQL statement because it was
not a valid Command Line Processor command. During SQL processing it
returned: SQL0109N The statement or command was not processed because
the following clause is not supported in the context where it is
used: "WITH ISOLATION USE AND KEEP". LINE NUMBER=9. SQLSTATE=42601

Specifying the isolation level:
For static SQL:
If an isolation-clause is specified in the statement, the value of that clause is used.
If an isolation-clause is not specified in the statement, the isolation level that was specified for the package when the package was bound to the database is used.
You need to bind the routine package with UR, since your DECLARE GTT statement is static. Before CREATE OR REPLACE use the following in the same session:
CALL SET_ROUTINE_OPTS('ISOLATION UR')
P.S.: If you want to run your routine not only 1 time in the same session without an error, use additional WITH REPLACE option of DECLARE.

If your Db2 server runs on Linux/Unix/Windows (Db2-LUW), then there is no such statement as SET OPTION COMMIT=*CHG , and so Db2 will throw an exception for that invalid syntax.
It is important to only use the matching Db2 Knowledge Centre for your Db2 platform and your Db2-version. Don't use Db2-Z/OS documentation for Db2-LUW development. The syntax and functionalities differ per platform and per version.
A Db2-LUW SQL PL procedure can use with ur in its internal queries, and if you are getting an error then something else is wrong. You have to use with ur in the correct syntax however, i.e in a statement that supports this clause. For your example you get the error because the clause does not appear to be valid in the depicted context. You can achieve the desired result in other ways, one of them being to populate the table in a separate statement from the declaration (e.g insert into session.temp_table("Trade ID") select uti from xyz with ur; ) and other ways are also possible.
One reason to use the online Db2 Knowledge Cenbtre documentation is that it includes sample programs, including sample SQL PL procedures, which are also available in source code form in the sample directory of your DB2-LUW server, in addition to being available on github. It is wise to study these, and get them working for you.

Related

DB2 stored procedure gave syntax errors

I am creating a stored procedure for db2. But it is giving an error saying that
"SQL Error [42601]: An unexpected token "END-OF-STATEMENT" was found
following "SS_TOKEN_BAK". Expected tokens may include: " END IF"..
SQLCODE=-104, SQLSTATE=42601, DRIVER=4.23.42".
Following is my stored procedure.
CREATE OR REPLACE PROCEDURE TOKEN_CLEANUP_SP
BEGIN
DECLARE batchSize INTEGER;
-- ------------------------------------------
-- CONFIGURABLE ATTRIBUTES
-- ------------------------------------------
SET batchSize = 10000; -- SET BATCH SIZE FOR AVOID TABLE LOCKS [DEFAULT : 10000]
-- ------------------------------------------------------
-- BACKUP IDN_OAUTH2_ACCESS_TOKEN TABLE
-- ------------------------------------------------------
IF EXISTS (SELECT TABLE_NAME FROM TABLES WHERE TABLE_NAME = 'IDN_OAUTH2_ACCESS_TOKEN_BAK')
THEN
DROP TABLE IDN_OAUTH2_ACCESS_TOKEN_BAK;
END IF;
END/
Is anyone face this type of issue. Any help on this would be much appreciated.
Verify that you have the end-of-statement delimiter configured correctly for whatever tool submits the 'CREATE OR REPLACE' procedure. Different tools have different ways to configure the block terminator (alternatively known end of statement delimiter). For command line scripts, use --#SET TERMINATOR / at the start of the file, other ways are possible.
Your code sample shows / as the block terminator, so you might want to use that character as the block delimiter. The semi-colon terminates statements inside the block.
Separately you should see that your code won't compile if the specified table does not exist in the implied schema at compilation time, because you are using static SQL. You may want to use dynamic SQL instead for the drop table statement (search for 'EXECUTE IMMEDIATE' examples).

How to "select" inside a stored procedure or a UDF in DB2 LUW?

I believe this question is very trivial. I' unable to select rows inside a stored procedure that I defined, nor inside a UDF. Here is the syntax that I used:
Create Or Replace Procedure GenerateSequence(
In InternalUnitID SmallInt,
In ObjectTypeID SmallInt)
Language SQL
Dynamic Result Sets 1
Not Deterministic
No External Action
Modifies SQL Data
Commit On Return Yes
Begin
Select Number
From Sequence
Where InternalUnit=InternalUnitID
And ObjectType=ObjectTypeID;
End
If I try to create the above procedure (by putting the definition in a SQL file and running it). I get the following error message:
DB21034E The command was processed as an SQL statement because it was not a valid Command Line Processor command. During SQL processing it returned:
SQL0104N An unexpected token "Select Number From Sequence Where Intern" was
found following "n Return Yes Begin ". Expected tokens may include: "".
LINE NUMBER=21. SQLSTATE=42601
Any clue what can be the cause of this problem?
My environment is DB2 10.5 Express on Windows.
My problem was that I needed to use a cursor in order to return the result set to the caller of the stored procedure.
References:
CREATE PROCEDURE (SQL) statement
Compound SQL (compiled) statement

Getting results from Oracle stored procedure insertion through pyodbc

I am using pyodbc (version 3.0.7) to access an Oracle (version 11g) database. We are writing stored procedures to handle the insertions. The primary keys for inserted objects are assigned with triggers, so we want to get the newly-inserted object's primary key into python after the stored procedure is called by the python script. (Due to client requirements, we don't have the flexibility of changing database, libraries, etc.)
According to the pyodbc documentation, return (OUT) parameters in stored procedures are not supported. Neither are stored functions. The documentation suggests to add a SELECT statement to the end of a stored procedure to get results out. However, we are new to SQL scripting, and Google searching for the last two days has turned up a lot of information for SQLServer and other databases, but next to nothing for Oracle. Trying the SQLServer examples on the Oracle db has not been tremendously helpful, as the Oracle SQL Developer shows various errors with the syntax (DECLARE where one shouldn't be, INTO required for SELECT statements, etc.).
Ultimately, we want the stored procedure to insert a new object, and then we want to somehow get the newly-created primary key for that object.
Here is an example of a stored procedure that correctly inserts an object (note that if obj_id is given as "None" in python, then the object is assigned a new primary key by a trigger):
CREATE OR REPLACE PROCEDURE insert_an_obj (an_obj_id NUMBER) AS
new_primary_key NUMBER;
BEGIN
INSERT INTO OBJS (OBJ_ID) VALUES (an_obj_id) RETURNING OBJ_ID INTO new_primary_key;
-- A SELECT statement should go here in order to get the new value for new_primary_key.
END insert_an_obj;
Supposedly, a SELECT statement at the end of the stored procedure will make it so the next time my script calls cursor.fetchall(), the script would get a list of whatever was selected. However, I have been unable to get this to work. Some failed SELECT examples (one of which might go in the stored procedure above in place of the SELECT comment) include the following:
-- These fail to compile because SQL Developer doesn't like them (though various sources online said that they work on SQLServer):
SELECT * FROM OBJS WHERE OBJ_ID=new_primary_key;
SELECT OBJ_ID FROM OBJS WHERE OBJ_ID=new_primary_key;
Like I said, I'm new to SQL, and likely I just need to know the proper syntax to get the SELECT statement working nicely in Oracle. Any suggestions? Or is there something that I'm misunderstanding?
As mentioned by Justin Cave in the comment above, "you can't just put a SELECT in a stored procedure to return data to the client." At least not with Oracle 11g. He continues: "In 11g, the only way to regurn data from a stored procedure is to have an OUT parameter", which AFIK, not possible using version 3.0.7 of pyodbc.

Selecting data from a different schema within a stored procedure

Consider this:
CREATE PROCEDURE [dbo].[setIdentifier](#oldIdentifierName as varchar(50), #newIdentifierName as varchar(50))
AS
BEGIN
DECLARE #old_id as int;
DECLARE #new_id as int;
SET #old_id = (SELECT value FROM Configuration WHERE id = #oldIdentifierName);
SET #new_id = (SELECT value FROM Configuration WHERE id = #newIdentifierName);
IF #old_id IS NOT NULL AND #new_id IS NOT NULL
BEGIN
UPDATE Customer
SET type = #new_id
WHERE type = #old_id;
END;
END
[...]
EXECUTE dbo.setIdentifier '1', '2';
What this does is create a stored procedure that accepts two parameters which it then uses to update a Customer table.
The problem is that the entire script above runs within a schema other than "dbo". Let's just assume the schema is "company1". And when the stored procedure is called, I get an error from the SELECT statement, which says that the Configuration table cannot be found. I'm guessing this is because MS SQL by default looks for tables within the same schema as the location of the stored procedure, and not within the calling context.
My question is this:
Is there some option or parameter or switch of some kind that will
tell MS SQL to look for tables in the "caller's default schema" and
not within the schema that procedure itself is stored in?
If not,
what would you recommend? I don't really want to prefix the tables
with the schema name, because it would be kind of unflexible to do
that. So I'm thinking about using dynamic sql (and the schema_name()
function which returns the correct value even within the procedure),
but I am just not experienced enough with MS SQL to construct the
proper syntax.
It would be a tad more efficient to explicitly specify the schema name. And generally speaking, schema's are mainly used to divide a database into logical area's. I would not anticipate on tables schema-hopping often.
Regarding your question, you might want to have a look at the 'execute as' documentation on msdn, since it allows to explicitly control your execution context.
I ended up passing the schema name to my script as a property on the command line for the "sqlcmd" command. Like this:
C:/> sqlcmd -vSCHEMANAME=myschema -imysqlfile
In the SQL script I can then access this variable like this:
SELECT * from $(SCHEMANAME).myTable WHERE.... etc
Not quite as flexible as dynamic sql, but "good enough" as it were.
Thanks all for taking time to respond.

Compound Statement unable to change the delimiter

I am attempting to run a simple compound statement within the Query Editor of DB Solo 4.2.2
It appears I am unable to properly change the end of line delimiter. I am using DB2. Here is a simple example that gives the error:
--#SET TERMINATOR #
BEGIN ATOMIC
DECLARE id INT;
SET id = 10;
END #
--#SET TERMINATOR ;
Error is:
An unexpected token "INT" was found following "N ATOMIC DECLARE id". Expected tokens may include: "END-OF-STATEMENT"
Thanks in advance
DB2 only allows the semicolon to be used as a delimiter in Compound SQL. The syntax you are using appears to only be valid when using the db2batch utility (which comes with DB2 Linux/Unix/Windows).
Here is some relevant information from the Information Center (this is from the z/OS IC):
How to code multiple statements in an SQL procedure
Use a semicolon
character to separate SQL statements within an SQL procedure.
The procedure body has no terminating character. Therefore, if the
procedure contains only one statement, you do not need to put a
semicolon after that statement. If the procedure consists of a set of
nested statements, you do not need to put a semicolon after the
outermost statement.