How to write a stored procedure in DB2 to perform UPDATE / DELETE operation by passing Dynamic values? - stored-procedures

I want to write a stored procedure in DB2 database for UPDATE and DELETE operations.
I am able to do that by directly providing values in procedure, but I want to do it by passing dynamic values.
My Table Structure is -
create table emp2 (int_1 int, char_1 char(10))
Below is my stored procedure for UPDATE operation which I am able to run but, its not behaving as per expectations. Changes are not reflecting in DB even after passing correct parameters while calling stored procedure.
CREATE OR REPLACE PROCEDURE "DB2INST1"."UPDATE_1" (IN int_1 int, IN
char_1 char(10)) SPECIFIC UPDATE_1
LANGUAGE SQL
DYNAMIC RESULT SETS 1
BEGIN
update emp2 set char_1=char_1 where int_1=int_1;
END;
This is the my stored procedure for DELETE operation which I am able to run successfully, but it's deleting all rows from the database table instead of deleting a single row:
CREATE OR REPLACE PROCEDURE "DB2INST1"."DELETE_1" (IN int_1 int)
SPECIFIC DELETE_1
LANGUAGE SQL
DYNAMIC RESULT SETS 1
BEGIN
delete from emp2 where int_1=int_1;
END;
Please provide me syntax for creating stored procedure for UPDATE and DELETE operations by passing dynamic values in a DB2 database.

The problem is that you don't qualify your columns and parameters having the same names.
According to the References to SQL parameters, SQL variables, and global variables:
Names that are the same should be explicitly qualified. Qualifying a
name clearly indicates whether the name refers to a column, SQL
variable, SQL parameter, row variable field, or global variable. If
the name is not qualified, or qualified but still ambiguous, the
following rules describe whether the name refers to a column, an SQL
variable, an SQL parameter, or a global variable:
If the tables and views specified in an SQL routine body exist at the time the routine is created, the name is first checked as a column
name. If not found as a column, it is then checked as an SQL variable
in the compound statement, then checked as an SQL parameter, and then,
finally, checked as a global variable.
That is, the following statements changing all the table rows obviously are equivalent:
update emp2 set char_1 = char_1 where int_1 = int_1;
--and
update emp2 e set e.char_1 = e.char_1 where e.int_1 = e.int_1;
What you need is to rewrite this statement to the following, if you want to use the same column and parameter names (the routine name UPDATE_1 is used here for the parameter qualification).
update emp2 set char_1 = UPDATE_1.char_1 where int_1 = UPDATE_1.int_1
fiddle

Related

Bigquery - parametrize tables and columns in a stored procedure

Consider an enterprise that captures sensor data for different production facilities. per facility, we create an aggregation query that averages the values to 5min timeslots. This query exists out of a long list of with-clauses and writes data to a table (called aggregation_table).
Now my problem: currently we have n queries running that exactly run the same logic, the only thing that differs are table names (and sometimes column names but let's ignore that for now).
Instead of managing n different scripts that are basically the same, I would like to put it in a stored procedure that is able to work like this:
CALL aggregation_query(facility_name) -> resolve the different tables for that facility and then use them in the different with clauses
On top of that, instead of having this long set of clauses that give me the end-result, I would like to chunk them up in logical blocks that are parametrizable, So for example, if I call the aforementioned stored_procedure for facility A, I want to be able to pass / use this table name in these different functions, where the output can be re-used in the next statement (like you would do with with clauses).
Another argument of why I want to chunk this up in re-usable blocks is because we have many "derivatives" on this aggregation query, for example to manage historical data, to correct data or to have the sensor data on another aggregation level. As these become overly complex, it is much easier to manage them without having to copy paste and adjust these every time.
In the current set-up, it could be useful to know that I am only entitled to use plain BigQuery, As my team is not allowed to access the CI/CD / scheduling and repository. (meaning that I cannot solve the issue by having CI/CD that deploys the n different versions of the procedure and functions)
So in the end, I would like to end up with something like this using only bigquery:
CREATE OR REPLACE PROCEDURE
`aggregation_function`()
BEGIN
DECLARE
tablename STRING;
DECLARE
active_table_name STRING; ##get list OF tables CREATE TEMP TABLE tableNames AS
SELECT
table_catalog,
table_schema,
table_name
FROM
`catalog.schema.INFORMATION_SCHEMA.TABLES`
WHERE
table_name = tablename;
WHILE
(
SELECT
COUNT(*)
FROM
tableNames) >= 1 DO ##build dataset + TABLE name
SET
active_table_name = CONCAT('`',table_catalog,'.',table_schema,'.' ,table_name,'`'); ##use concat TO build string AND execute
EXECUTE IMMEDIATE '''
INSERT INTO
`aggregation_table_for_facility` (timeslot, sensor_name, AVG_VALUE )
WITH
STEP_1 AS (
SELECT
*
FROM
my_table_function_step_1(active_table_name,
parameter1,
parameter2) ),
STEP_2 AS (
SELECT
*
FROM
my_table_function_step_2(STEP_1,
parameter1,
parameter2) )
SELECT * FROM STEP_2
'''
USING active_table_name as active_table_name;
DELETE
FROM
tableNames
WHERE
table_name = tablename;
END WHILE
;
END
;
I was hoping someone could make a snippet on how I can do this in Standard SQL / Bigquery, so basically:
stored procedure that takes in a string variable and is able to use that as a table (partly solved in the approach above, but not sure if there are better ways)
(table) function that is able to take this table_name parameter as well and return back a table that can be used in the next with clause (or alternatively writes to a temp table)
I think below code snippets should provide you with some insights when dealing with procedures, inserts and execute immediate statements.
Here I'm creating a procedure which will insert values into a table that exists on the information schema. Also, as a value I want to return I use OUT active_table_name to return the value I assigned inside the procedure.
CREATE OR REPLACE PROCEDURE `project-id.dataset`.custom_function(tablename STRING,OUT active_table_name STRING)
BEGIN
DECLARE query STRING;
SET active_table_name= (SELECT CONCAT('`',table_catalog,'.',table_schema,'.' ,table_name,'`')
FROM `project-id.dataset.INFORMATION_SCHEMA.TABLES`
WHERE table_name = tablename);
#multine query can be handled by using ''' or """
Set query =
'''
insert into %s (string_field_0,string_field_1,string_field_2,string_field_3,string_field_4,int64_field_5)
with custom_query as (
select string_field_0,string_field_2,'169 BestCity',string_field_3,string_field_4,55677 from %s limit 1
)
select * from custom_query;
''';
# querys must perform operations and must be the last thing to perform
# pass parameters using format
execute immediate (format(query,active_table_name,active_table_name));
END
You can also use a loop to iterate trough records from a working table so it will execute the procedure and also be able to get the value from the procedure to use somewhere else.ie:A second procedure to perform a delete operation.
DECLARE tablename STRING;
DECLARE out_value STRING;
FOR record IN
(SELECT tablename from `my-project-id.dataset.table`)
DO
SET tablename = record.tablename;
LOOP
call `project-id.dataset`.custom_function(tablename,out_value);
select out_value;
END LOOP;
END FOR;
To recap, there are some restrictions such as the possibility to call procedures inside a execute immediate or to use execute immediate inside an execute immediate, to count a few. I think these snippets should help you dealing with your current situation.
For this sample I use the following documentation:
Data Manipulation Language
Dealing with outputs
Information Schema Tables
Execute Immediate
For...In
Loops

how to write to dynamically created table in Redshift procedure

I need to write a procedure in Redshift that will write to a table, but the table name comes from the input string. Then I declare a variable that puts together the table name.
CREATE OR REPLACE PROCEDURE my_schema.data_test(current "varchar")
LANGUAGE plpgsql
AS $$
declare new_table varchar(50) = 'new_tab' || '_' || current;
BEGIN
select 'somestring' as colname into new_table;
commit;
END;
$$
This code runs but it doesn't create a new table, no errors. If I remove the declare statement then it works, creating a table called "new_table". It's just not using the declared variable name.
It's hard to find good examples because Redshift is postgresql and all the postgresql pages say that it only has functions, not procedures. But Redshift procedures were introduced last year and I don't see many examples.
Well, when you are declaring a variable "new_table", and performing a SELECT ..INTO "new_table", the value is getting assigned to the variable "new_table". You will see that if you return your variable using a OUT parameter.
And when you remove the declaration, it simply work as a SELECT INTO syntax of Redshift SQL and creates a table.
Now to the solution:
Create a table using the CREATE TABLE AS...syntax.
Also you need to pass the value of declared variable, so use the EXECUTE command.
CREATE OR REPLACE PROCEDURE public.ct_tab (vname varchar)
AS $$
DECLARE tname VARCHAR(50):='public.swap_'||vname;
BEGIN
execute 'create table ' || tname || ' as select ''name''';
END;
$$ LANGUAGE plpgsql
;
Now if you call the procedure passing 'abc', a table named "swap_abc" will be created in public schema.
call public.ct_tab('abc');
Let me know if it helps :)

DB2 Stored Procedure Not able to assign data to a variable

I have a simple stored procedure to calculate the sum of salaries of employees, sum of their squares and number of rows.
This is the stored procedure I have written:
I get an error in fetching the number of rows from the database and assigning it to a variable. What do I do? Using DB2 11.5
It helps to specify the exact error code when asking questions (don't write get an error, do write instead 'get error SQL0104N ...`.
Your mistake is that you have not followed the documented order for SQL statements in compound SQL blocks.
The SELECT statement can only appear after any cursor definitions, local procedures , and handlers if you have any.
So move the statement SELECT COUNT(*) INTO TOTAL_ROWS FROM EMPLOYEE; so that it appears after the DECLARE CURSOR1 ... line, the try to recompile.

Evaluating datetime register during trigger event

I was testing the following behaviour for datetime special register (stated here)
If the SQL statement in which a datetime special register is used is
in a user-defined function or stored procedure that is within the
scope of a trigger, Db2 uses the timestamp for the triggering SQL
statement to determine the special register value.
So i crated a table with a timestamp field, a stored procedure (native sql) that is inserting the same 10 rows to the table and the tamestamp column is given the value of "current timestamp". Then i created a trigger on some other table (after insert trigger).
The result is 10 rows with increasing timestamp. I expected the timestamp to be the same as in my interpretation the stored procedure was in the scope of a trigger.
Can you help me what this statement means?
create trigger date_check
after insert on test
for each row mode db2sql
call date_sp2()
create procedure date_sp2()
language sql
BEGIN
declare i smallint default 0;
my_loop: LOOP
insert into empty_char values('Y','Y','Y','Y',current date, current timestamp);
SET I = I + 1;
IF I = 10 THEN LEAVE my_loop;
END IF;
END LOOP my_loop;
END
My guess is that note 1 applies
https://www.ibm.com/support/knowledgecenter/SSEPEK_11.0.0/sqlref/src/tpc/db2z_currenttimestamp.html#fntarg_1
If this special register is used more than one time within a single SQL statement, or used with CURRENT DATE or CURRENT TIME within a single statement, all values are based on a single clock reading. ¹
¹ Except for the case of a non-atomic multiple row INSERT or MERGE statement.

Stored procedure for updating values dynamically in db2 table

I am trying to update the values of a table dynamically, using stored procedure. My stored procedure is as follows,
CREATE OR REPLACE PROCEDURE Update
(
IN ID1 BIGINT,
IN SOURCE1 VARCHAR(100),
IN NAME1 VARCHAR(100)
)
DYNAMIC RESULT SETS 2
LANGUAGE SQL
BEGIN
UPDATE MessageTable
SET SOURCE=SOURCE1,
NAME=NAME1
WHERE ID=ID1;
END
When I try to pass the value for ID1 and SOURCE1 alone, the values are not getting updated. When I pass all the three values, they are getting updated properly. My requirement is even if I pass two values it should get updated. I tried giving DEFAULT NULL for the arguments. Since the fields are declared NOT NULL, it was not working. Could someone help to overcome this. The stored procedure should work even if I pass single value. Thanks in advance.
You could use COALESCE:
UPDATE MessageTable
SET SOURCE = COALESCE(SOURCE1, SOURCE),
NAME = COALESCE(NAME1, NAME)
WHERE ID = ID1;
When NAME1 is NULL then the original NAME will be preserved.
You could check with IF or another statement whether all arguments have non-NULL values and then switch to either an UPDATE statement that sets one or two column values.

Resources