I have created a procedure like the one below. i know PL SQL a bit but i am new to DB2.
when i execute the below procedure getting an error that the use of reserve keyoword end is invalid.
CREATE PROCEDURE test()
LANGUAGE SQL
DYNAMIC RESULT SETS 1
WLM ENVIRONMENT FOR DEBUG MODE #TTCOM
ASUTIME NO LIMIT
BEGIN
DECLARE SQLSTATE CHAR(5) DEFAULT '00000';
DECLARE SQLCODE INTEGER DEFAULT 0;
DECLARE TABLE_DOES_NOT_EXIST_STATE CONDITION FOR SQLSTATE '42704';
DECLARE tname VARCHAR(5000);
DECLARE CONTINUE HANDLER FOR TABLE_DOES_NOT_EXIST_STATE BEGIN
END;
FOR i AS SELECT tablename FROM EWPS.tablelist
DO
tname = 'create table '+i.tablename+'_2112 like '+i.tablename+';' ;
tname1 = 'insert into '+i.tablename+'_2112 (select * from '+i.tablename+');';
call DBMS_OUTPUT.PUT(tname)
execute immediate tname;
execute immediate tname;
--set tname=i.tablename
END FOR;
--RETURN tname ;
--COMMIT;
END test;
any help is much appreciated
You need two differnt terminators - one for the statements within the procedure and one for the CREATE PPROCEDURE itself.
Check out this command to set a terminator
--#SET TERMINATOR #
And also chekc out this question & answers
Related
i am new for this, background is totally based on Oracle and related products. i have installed XAMPP 3.3.0, i have created a trigger which works fine.
DELIMITER $$
CREATE OR REPLACE
TRIGGER bi_cancel_booking BEFORE INSERT
ON cancelled_booking
FOR EACH ROW BEGIN
--
DECLARE next_receipt VARCHAR(20);
DECLARE rct_no INT(5);
--
SELECT MAX(starting_number)+1, CONCAT(receipt_prefix,'-',MAX(starting_number)+1)
INTO rct_no, next_receipt
FROM com_receipt_types
WHERE receipt_type = 'Cancel Booking';
END$$
DELIMITER ;
but when using NEW.cancel_receipt_no (see below code) it is showing error : Error Code: 1327 Undeclared variable: NEW
DELIMITER $$
CREATE OR REPLACE
TRIGGER bi_cancel_booking BEFORE INSERT
ON cancelled_booking
FOR EACH ROW BEGIN
--
DECLARE next_receipt VARCHAR(20);
DECLARE rct_no INT(5);
--
SELECT MAX(starting_number)+1, CONCAT(receipt_prefix,'-',MAX(starting_number)+1)
INTO rct_no, NEW.cancel_receipt_no
FROM com_receipt_types
WHERE receipt_type = 'Cancel Booking';
END$$
DELIMITER ;
is there any restriction that i cannot assign the value to NEW.field as in my first code here when i am trying to assign the value as: NEW.cancel_receipt_no := next_receipt then it also showing error. please help, how i can do this here?
i used below code and error has gone (used SET):
SET NEW.cancel_receipt_no = next_receipt;
DELIMITER $$
CREATE OR REPLACE
TRIGGER bi_cancel_booking BEFORE INSERT
ON cancelled_booking
FOR EACH ROW BEGIN
--
DECLARE next_receipt VARCHAR(20);
DECLARE rct_no INT(5);
--
SELECT MAX(starting_number)+1, CONCAT(receipt_prefix,'-',MAX(starting_number)+1)
INTO rct_no, next_receipt
FROM com_receipt_types
WHERE receipt_type = 'Cancel Booking';
SET NEW.cancel_receipt_no := next_receipt;
END$$
DELIMITER ;
it was because of my lack of knowledge, gradually i will learn the things with help of all seniors here.
I have a stored procedure in DB2 which returns a bunch of columns. I need to apply a 'WHERE' condition or do a sorting on one of the columns it returns. I don't want to touch the stored procedure and do this filtering/sorting when calling the stored procedure, something like below
select * from 'call SP1()' as T where T.column1 > 10
Is this possible in DB2?
Here is a deliberately artificial example of a pipelined UDF that filters the result-set of an SQLPL procedure.
In real world coding, most programmers will avoid filtering outside of stored-procedures, simply because it is easier and better performing, and more natural to filter at the earliest possible opportunity.
Tested on Db2-LUW v11.1.3.3 and 11.1.2.2 with DB2_COMPATIBILITY_MODE=ORA (or at least Bit-17 set to 1 as in 0x10000 , acknowledgement to P.Vernon for this clarification):
--#SET TERMINATOR #
create or replace procedure alltabs
dynamic result sets 1
language sql
specific alltabs
begin
declare v_cur cursor with return to caller for
select tabschema,tabname,type from syscat.tables ;
open v_cur;
end#
create or replace function allstatviews()
returns table (stat_view_name varchar(80))
begin
declare v_rs result_set_locator varying;
declare v_tabschema varchar(128);
declare v_tabname varchar(128);
declare v_type char(1);
declare sqlstate char(5) default '00000';
call alltabs;
associate result set locator (v_rs) with procedure alltabs;
allocate v_rscur cursor for result set v_rs;
fetch from v_rscur into v_tabschema, v_tabname, v_type;
while ( sqlstate = '00000') do
if v_type='V' and v_tabschema='SYSSTAT'
then
pipe(cast(rtrim(v_tabschema)||'.'||rtrim( v_tabname) as varchar(80)));
end if;
fetch from v_rscur into v_tabschema, v_tabname, v_type;
end while;
return;
end#
select * from table(allstatviews())
#
I have a function block like
DO $$
DECLARE tran_year RECORD;
BEGIN
FOR tran_year IN SELECT * FROM tbale1 loop
EXECUTE 'ALTER TABLE mytable ADD CONSTRAINT unique_cons$$aaf1c86 UNIQUE (samplecol)';
END LOOP;
END $$;
in Postgres, when trying to execute this block, I face an issue saying that ERROR: syntax error at or near "aaf1c86".
I also have the need to use $$ in constraint name. Any idea of overcoming this error
As documented in the manual you can use any character sequence between the $ signs if you use the same sequence to end the string literal.
So just use something different the $$ on the outside:
DO $doblock$
DECLARE tran_year RECORD;
BEGIN
FOR tran_year IN SELECT * FROM tbale1 loop
EXECUTE 'ALTER TABLE mytable ADD CONSTRAINT unique_cons$$aaf1c86 UNIQUE (samplecol)';
END LOOP;
END $doblock$;
I want to use my trigger for logging purposes - every time, when a user inserts something or makes updates, there should appear a new row in my history table. I already have a trigger, which works good. Now it runs every time, when I do inserts. This is how it looks like:
CREATE OR REPLACE FUNCTION public.trigger_history_insert()
RETURNS trigger AS
$BODY$
DECLARE
...
BEGIN
... it does a lot of things here and works absolutely correctly
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION public.trigger_history_insert()
OWNER TO postgres;
Now I want to add logging into this trigger. I tried to follow these two official tutorials ([1], [2]), but ended in failure. This was my first attempt:
DECLARE
...
BEGIN
... everything remains unchanged except this part
EXEC SQL BEGIN DECLARE SECTION;
const char *stmt = 'INSERT INTO history (field1) VALUES (?)';
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE mystmt FROM :stmt;
EXEC SQL EXECUTE mystmt USING new.field1;
END;
In this case I get syntax error pointing to this line of code:
EXEC SQL BEGIN DECLARE SECTION;
^
This was my second attempt:
BEGIN
... everything remains unchanged except this part
BEGIN DECLARE SECTION;
const char *stmt = 'INSERT INTO history (field1) VALUES (?)';
END DECLARE SECTION;
PREPARE mystmt FROM :stmt;
EXECUTE mystmt USING new.field1;
END;
Now, I get an error message pointing to this line:
BEGIN DECLARE SECTION;
^
This was my third attempt:
$BODY$
DECLARE
...
const char *stmt = 'INSERT INTO history (field1) VALUES (?)'
BEGIN
...
PREPARE mystmt FROM :stmt;
EXECUTE mystmt USING new.field1;
END
Now I get an error message pointing to this line:
const char *stmt = 'INSERT INTO history ...
^
This was my last attempt:
$BODY$
DECLARE
...
stmt text := 'INSERT INTO history (field1) VALUES (?)'
BEGIN
...
PREPARE mystmt FROM :stmt;
EXECUTE mystmt USING new.field1;
END
And in this final case, the error message points to this line:
PREPARE mystmt FROM :stmt;
^
So, what am I doing wrong and how can I fix it?
You are mixing up PL/pgSQL, a language for writing functions in the database, and ecpg, which is used for embedding database access in C client code.
The correct solution would look like this:
INSERT INTO history (field1) VALUES (NEW.field1);
I am adjusting some PL/pgSQL code so my refcursor can take the table name as parameter. Therefore I changed the following line:
declare
pointCurs CURSOR FOR SELECT * from tableName for update;
with this one:
OPEN pointCurs FOR execute 'SELECT * FROM ' || quote_ident(tableName) for update;
I adjusted the loop, and voilĂ , the loop went through. Now at some point in the loop I needed to update the record (pointed by the cursor) and I got stuck. How should I properly adjust the following line of code?
UPDATE tableName set tp_id = pos where current of pointCurs;
I fixed the quotes for the tableName and pos and added the EXECUTE clause at the beginning, but I get the error on the where current of pointCurs.
Questions:
How can I update the record?
The function was working properly for tables from the public schema and failed for tables from other schemas (e.g., trace.myname).
Any comments are highly appreciated..
Answer for (i)
1. Explicit (unbound) cursor
EXECUTE is not a "clause", but a PL/pgSQL command to execute SQL strings. Cursors are not visible inside the command. You need to pass values to it.
Hence, you cannot use the special syntax WHERE CURRENT OFcursor. I use the system column ctid instead to determine the row without knowing the name of a unique column. Note that ctid is only guaranteed to be stable within the same transaction.
CREATE OR REPLACE FUNCTION f_curs1(_tbl text)
RETURNS void AS
$func$
DECLARE
_curs refcursor;
rec record;
BEGIN
OPEN _curs FOR EXECUTE 'SELECT * FROM ' || quote_ident(_tbl) FOR UPDATE;
LOOP
FETCH NEXT FROM _curs INTO rec;
EXIT WHEN rec IS NULL;
RAISE NOTICE '%', rec.tbl_id;
EXECUTE format('UPDATE %I SET tbl_id = tbl_id + 10 WHERE ctid = $1', _tbl)
USING rec.ctid;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Why format() with %I?
There is also a variant of the FOR statement to loop through cursors, but it only works for bound cursors. We have to use an unbound cursor here.
2. Implicit cursor in FOR loop
There is normally no need for explicit cursors in plpgsql. Use the implicit cursor of a FOR loop instead:
CREATE OR REPLACE FUNCTION f_curs2(_tbl text)
RETURNS void AS
$func$
DECLARE
_ctid tid;
BEGIN
FOR _ctid IN EXECUTE 'SELECT ctid FROM ' || quote_ident(_tbl) FOR UPDATE
LOOP
EXECUTE format('UPDATE %I SET tbl_id = tbl_id + 100 WHERE ctid = $1', _tbl)
USING _ctid;
END LOOP;
END
$func$ LANGUAGE plpgsql;
3. Set based approach
Or better, yet (if possible!): Rethink your problem in terms of set-based operations and execute a single (dynamic) SQL command:
-- Set-base dynamic SQL
CREATE OR REPLACE FUNCTION f_nocurs(_tbl text)
RETURNS void AS
$func$
BEGIN
EXECUTE format('UPDATE %I SET tbl_id = tbl_id + 1000', _tbl);
-- add WHERE clause as needed
END
$func$ LANGUAGE plpgsql;
SQL Fiddle demonstrating all 3 variants.
Answer for (ii)
A schema-qualified table name like trace.myname actually consists of two identifiers. You have to
either pass and escape them separately,
or go with the more elegant approach of using a regclass type:
CREATE OR REPLACE FUNCTION f_nocurs(_tbl regclass)
RETURNS void AS
$func$
BEGIN
EXECUTE format('UPDATE %s SET tbl_id = tbl_id + 1000', _tbl);
END
$func$ LANGUAGE plpgsql;
I switched from %I to %s, because the regclass parameter is automatically properly escaped when (automatically) converted to text.
More details in this related answer:
Table name as a PostgreSQL function parameter