db2 dynamic sql with select into clause in anonymous block - sql

I am working on a DB2 SQL code.
I need to get the count of records from a list of tables. The table details will be fetched from a cursor with select.
To get the count of records I have been trying with a SELECT INTO statement. Since the table names will be varying, I am using a dynamic SQL code.
I am sharing the piece of code that I have been trying.
I am not quite sure of the syntax while using DB2 SELECT INTO and Dynamic SQL combination. I am getting the following error with the below attempt.
Can anyone tell me why this is so? If possible, appreciate if you could share a working code of DB2 select into and dynamic sql.
SQL0104N An unexpected token "statmnt2" was found following "SET statmnt2 := 'set ? = (SELECT COD_TIPO_ARQU FROM '||indbn". Expected tokens may include: "".
DECLARE
indbnm VARCHAR(30);
intblnm VARCHAR(30);
v_errorText varchar2(50);
statmnt2 VARCHAR(1000);
VAR_COD_TIPO_ARQU CHAR(1);
stmt1 STATEMENT;
statmnt2 VARCCHAR2(100);
BEGIN
indbnm := "db2inst5";
intblnm:= "rules";
SET statmnt2 := 'set ? = (SELECT COD_TIPO_ARQU FROM '||indbnm||'.'||intblnm||' FETCH FIRST 1 ROWS ONLY)';
PREPARE stmt1 FROM statmnt2;
EXECUTE stmt1 into VAR_COD_TIPO_ARQU ;
DBMS_OUTPUT.PUT_LINE(VAR_COD_TIPO_ARQU);
EXCEPTION
WHEN OTHERS THEN
v_errorText :=SUBSTR(SQLERRM,1, 1024);
DBMS_OUTPUT.PUT_LINE('FAILED WITH MESSAGE: '||v_errorText);
END;

Related

Execute immediate with variable throwing error ORA-00936: missing expression

Getting missing expression error when my procedure reaches this statement.
ORA-00936: missing expression
What is wrong with this statement?
EXECUTE IMMEDIATE 'SELECT /*+ parallel (8) */ COUNT(1)
INTO '||v_datacount_backuptable||
' FROM cs_transassignment_26weeks';
What you are trying to achieve is unclear. In any case, you cannot pass a variable name as a variable.
If you want to feed the count into the variable from a fixed query, then you can skip the EXECUTE IMMEDIATE: it is meant to execute a query that is dynamically composed, which is not the case in the code that you showed:
declare v_datacount_backuptable int;
begin
SELECT /*+ parallel (8) */ COUNT(1) INTO v_datacount_backuptable FROM cs_transassignment_26weeks;
dbms_output.put_line(v_datacount_backuptable);
end;
/
If you actually built the query somewhere else and you need to execute it, then, as answered by Littlefoot already, you need to move the INTO clause out of the query string:
declare
v_datacount_backuptable int;
v_query varchar(500);
begin
v_query := 'SELECT /*+ parallel (8) */ COUNT(1) FROM cs_transassignment_26weeks';
EXECUTE IMMEDIATE v_query INTO v_datacount_backuptable;
dbms_output.put_line(v_datacount_backuptable);
end;
/
Demo on DB Fiddle
Should be
EXECUTE IMMEDIATE 'SELECT /*+ parallel (8) */ COUNT(1) FROM cs_transassignment_26weeks'
INTO v_datacount_backuptable;
i.e. INTO is outside of the statement being executed.

I want to get count for rows in cursor in DB2

CREATE OR REPLACE PROCEDURE USP_TEST_ROW_COUNT(
OUT vROW_COUNT BIGINT
)
RESULT SETS 1
MODIFIES SQL DATA
LANGUAGE SQL
P1: BEGIN ATOMIC
BEGIN
DECLARE C1 CURSOR WITH RETURN FOR
SELECT * FROM TABLE_NAME;
OPEN C1;
SET vROW_COUNT = CURSOR_ROWCOUNT(C1);
END;
END P1
Above is my code but it is showing Below Error
DB2 SQL error: SQLCODE: -206, SQLSTATE: 42703, SQLERRMC: C1
Message: "C1" is not valid in the context where it is used.
Line: 12
Please Help.
You may insert the results into some DGTT if you want to return all rows in the result set and return the number of output rows simultaneously:
CREATE OR REPLACE PROCEDURE USP_TEST_ROW_COUNT(OUT vROW_COUNT BIGINT)
RESULT SETS 1
MODIFIES SQL DATA
BEGIN ATOMIC
DECLARE v_stab VARCHAR(100) DEFAULT 'SESSION.USP_TEST_ROW_COUNT';
DECLARE v_stmt VARCHAR(100) DEFAULT 'SELECT * FROM TABLE_NAME';
DECLARE C1 CURSOR WITH RETURN FOR S1;
EXECUTE IMMEDIATE
'DECLARE GLOBAL TEMPORARY TABLE '||v_stab||' AS ('
||v_stmt
||' ) DEFINITION ONLY WITH REPLACE ON COMMIT PRESERVE ROWS NOT LOGGED';
EXECUTE IMMEDIATE 'INSERT INTO '||v_stab||' '||v_stmt;
GET DIAGNOSTICS vROW_COUNT=ROW_COUNT;
PREPARE S1 FROM 'SELECT * FROM '||v_stab;
OPEN C1;
END#
CURSOR_ROWCOUNT can only return the number of rows fetched (by the caller of the stored procedure). This is different from the number of rows in the result set. So if your syntax was accepted the value would be zero initially as nothing as yet been fetched.
You can see an example here, which shows the cursor variable, the cursor being opened and fetched, and the resulting value returned by CURSOR_ROWCOUNT.
To find the number of rows in the result-set either consume the cursor (fetch until no more rows), or do a second query that counts the rows in the same unit of work, or append the count to each row and fetch only 1 row.

OPEN emp_refcur FOR {plsql variable} giving error

I am novice in plsql development.
I have a problem opening cursor on plsql variable (which has my dynamic SQL query). I am on ORACLE 12 i believe.
Details below:
emp_refcur SYS_REFCURSOR; -- Cursor declaration
component_sql:= 'select * from emp'; -- Base SQL
-- dynamic sql based on business logic
IF SOME_CONDITION THEN
filters:= ' where DEP IN (''ABC'',''DEF'')';
ELSE
filters:= ' where age>50';
component_sql:= component_sql||filters; -- appending filter to my base sql
OPEN emp_refcur FOR component_sql; -- opening the cursor
LOOP
FETCH emp_refcur INTO result;
EXIT WHEN emp_refcur%NOTFOUND;
dbms_output.put_line(result);
END LOOP;
CLOSE emp_refcur;
Compiling this procedure shown error at line 'OPEN emp_refcur FOR component_sql' and error are
Error(90,9): PL/SQL: Statement ignored
Error(90,29): PLS-00382: expression is of wrong type
I do not want to use bind values like
'OPEN emp_refcur FOR p_query_string USING p_deptno, p_sal;'
I have tried multiple approaches
OPEN emp_refcur FOR ''||component_sql||''; -- This approach is not causing compilation error, but running the procedure resulting
ORA-00900: invalid SQL statement
AND
OPEN emp_refcur FOR 'select * from emp'||filters; -- This is resulting the error
ORA-00900: invalid SQL statement
Not sure what i am missing here. Please help.
NOTE: Please ignore if there are any SQL query syntax errors. Because the SQL which i am printing after appending the filters is executing fine and getting results when i run separately.
I tried with your code and it has no issues. PLS-00382: expression is of wrong type --> This is a syntax error. It has nothing to do with bind variables. What is the declaration for result? I don't see in the snippet.
PLS-00382: expression is of wrong type
Cause: An expression has the wrong datatype for the context in which it was found.
Action: Change the datatype of the expression. You might want to use datatype conversion functions.
Declare
emp_refcur SYS_REFCURSOR;
filters varchar2(100);
component_sql varchar2(100);
result varchar2(20);
Begin
component_sql:= 'select 1 from dual'; -- Base SQL
-- dynamic sql based on business logic
IF 1=1 THEN
filters:= ' UNION ALL SELECT 2 from dual';
ELSE
filters:= ' where age>50';
End if;
component_sql:= component_sql||filters; -- appending filter to my base sql
OPEN emp_refcur FOR component_sql ; -- opening the cursor
LOOP
FETCH emp_refcur INTO result;
EXIT WHEN emp_refcur%NOTFOUND;
dbms_output.put_line(result);
END LOOP;
CLOSE emp_refcur;
END;

Test if anything was updated in dynamic query

I need to construct and execute an UPDATE statement dynamically. Then I need to test whether anything was updated at all. My code is as follows:
DECLARE
v_table_name text;
v_column_name text;
v_debug_flag boolean;
v_upd_stmt text;
BEGIN
select nm_table_name, replace(nm_table_name, 'CDB_', 'ID_')
into strict v_table_name, v_column_name
from m_entity e
where id=12;
v_upd_stmt := format('update %s set id_lock_usr =null, dt_lock=null where %s=$1 returning id_lock_usr',
v_table_name,
v_column_name);
execute v_upd_stmt using p_id;
END
How to know if anything was updated?
How to know if anything was updated ?
Various options. In a plpgsql function, you can check the special variable FOUND to see if the last SQL command affected any rows.
IF FOUND THEN ...
However, For dynamic queries with EXECUTE use GET DIAGNOSTICS instead. The manual:
Note in particular that EXECUTE changes the output of GET DIAGNOSTICS,
but does not change FOUND.
Related:
Dynamic SQL (EXECUTE) as condition for IF statement
Aside:
I see lingering problems with properly escaped identifiers (especially with upper case table names like you have), possibly even SQL injection. Fix with:
DECLARE
v_table_name text;
v_column_name text;
v_id_lock_usr integer; -- guessing the data type
i integer;
BEGIN
SELECT nm_table_name, replace(nm_table_name, 'CDB_', 'ID_')
INTO strict v_table_name, v_column_name
FROM m_entity e
WHERE id = 12;
EXECUTE format('UPDATE %I SET id_lock_usr = null, dt_lock = null
WHERE %I = $1 RETURNING id_lock_usr'
, v_table_name,
, v_column_name)
INTO v_id_lock_usr; -- to store the *single* result from RETURNING
GET DIAGNOSTICS i = ROW_COUNT;
IF i > 0 THEN
-- do something
END IF;
END
Note %I instead of %s.
In your case, if id_lock_usr returned by your query is NOT NULL (reliably), you might just test the result directly instead:
IF v_id_lock_usr IS NOT NULL ...
And you might want to schema-qualify table names to avoid ambiguities. But you must escape schema_name.table_name as two separate identifiers ..
Related:
Are PostgreSQL column names case-sensitive?
Define table and column names as arguments in a plpgsql function?
Table name as a PostgreSQL function parameter

DB2 dynamic table name

I want to count the rows of a number of tables. But the table name should be used dynamically. I want to do that within one SQL statement.
I tried it with
BEGIN ATOMIC
FOR tmp AS (
SELECT tabschema || '.' || tabname tbl
FROM syscat.tables WHERE tabname LIKE '%CD') DO
(SELECT COUNT(*) FROM tmp.tbl);
END FOR;
END
but I receive the error
DB21034E The command was processed as an SQL statement because it was not a
valid Command Line Processor command. During SQL processing it returned:
SQL0204N "TMP.TBL" is an undefined name. LINE NUMBER=1. SQLSTATE=42704
and found no other working solution...
Is there a solution for that?
Thanks in advance.
I assume that your SELECT COUNT(*) FROM tmp.tbl should translate in multiple statements like
select count(*) from TABLECD
select count(*) from TABLE2CD
...
However, your query will try to do a count of the table TBL in the schema TMP.
You'll have to prepare the complete SQL statement, store it in a variable and pass it to the PREPARE statement (documentation ).
A rather complete stored procedure which somewhat fits your requirements can be found here . The result of the counts will be stored in a table COUNTERS which you can query afterwards.
//edit: this is the example from the topic, adapt to work (not tested since I have no DB2 instance to test atm):
CREATE PROCEDURE tableCount()
LANGUAGE SQL
BEGIN
DECLARE SQLCODE INTEGER DEFAULT 0;
DECLARE SQLSTATE CHAR(5);
DECLARE vTableName VARCHAR(20);
DECLARE vTableCount INTEGER;
DECLARE stmt varchar(2000);
DECLARE not_found CONDITION FOR SQLSTATE '02000';
DECLARE c1 CURSOR FOR
SELECT tabname from syscat.tables where tabschema='DB2ADMIN';
DECLARE C2 CURSOR FOR S2
DECLARE CONTINUE HANDLER FOR not_found
SET stmt = '';
Delete from COUNTERS;
OPEN c1;
getRows:
LOOP
FETCH c1 INTO vTableName;
IF SQLCODE = 0 THEN
SET stmt ='SELECT Count(*) FROM ' || vTableName;
PREPARE S2 FROM stmt;
OPEN C2;
SET vTableCount = 0;
FETCH C2 INTO vTableCount;
INSERT INTO COUNTERS (tableName, tableCount)
VALUES (vTableName, vTableCount);
CLOSE C2;
ELSE
LEAVE getRows;
END IF;
END LOOP getRows;
CLOSE c1;
END