Refcursors in Postgresql - postgresql
Good Morning,
I have a quick question on how to see the data that is fetched in an Output refcursor
I'm executing the below block, its successfully executed but I want to see the data fetched in Output RefCursor, by the way I'm using PGADMIN 4 tool to connect to Postgres Servers,
BEGIN;
SELECT prod_package_list ('G6028|G6026|G6025|G6024|G6022|G6021|G6020', NULL);
FETCH ALL IN vref_cur;
COMMIT;
I found online and according to them I should follow the below format,
BEGIN;
SELECT test_prod_package_list ('G6028|G6026|G6025|G6024|G6022|G6021|G6020', NULL, 'vref_cur');
FETCH ALL IN "vref_cur";
COMMIT;
But the above execution block throws an error
function prod_package_list(unknown, unknown, unknown) does not exist,
The function is as follows,
CREATE OR REPLACE FUNCTION ssp2_pcat.prod_package_list(
prodlist text,
billcodelist text,
OUT vref_cur refcursor)
RETURNS refcursor
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
BEGIN
vref_cur := 'vref_cur';
IF prodlist IS NOT NULL THEN
OPEN vref_cur FOR
SELECT DISTINCT
b.package_id, a.product_id, a.pdct_ctlg_id, a.product_name, a.billing_system, a.billing_code, a.fulfill_system, a.fulfill_code, a.speed_code, a.product_type, a.product_type_value, a.return_auth_applies, a.return_auth_term, a.nrc_waiver, a.nrc_waiver_product_id, a.linked_nrc_product_id, a.national_east_west, a.lata_list, a.state_list, a.product_info_url, a.isp_prov_flags, a.product_coefficient, a.last_updated, a.product_keywords, a.combo_product_list, a.additional_info, a.usoc_info, a.min_billing_days, a.data_nrf_product_id, a.video_nrf_product_id, a.account_line_level, a.web_desc, a.orderguicd, a.prod_startdate, a.prod_enddate, b.pdct_ctlg_id, b.package_name, b.billing_code, b.category_id, b.standalone_cpe, b.standalone_truckroll, b.btn_billed, b.emp_discount, b.package_type, b.last_updated, b.pkg_detail_descr, b.grandfathered AS pkg_grandfathered,
CASE CONCAT_WS('', a.grandfathered, c.pkg_prod_grandfathered)
WHEN 'YY' THEN 'Y'
WHEN 'YN' THEN 'Y'
WHEN 'NY' THEN 'Y'
ELSE 'N'
END AS grandfathered
FROM ssp2_pcat.products AS a, ssp2_pcat.packages AS b, ssp2_pcat.package_products AS c
WHERE strpos(prodlist, CONCAT_WS('', '|', a.product_id, '|')) > 0
AND a.product_id = c.product_id AND c.package_id = b.package_id
AND c.start_date <= LOCALTIMESTAMP AND c.end_date >= LOCALTIMESTAMP
ORDER BY 1;
ELSIF billcodelist IS NOT NULL THEN
OPEN vref_cur FOR
SELECT DISTINCT
b.package_id, a.product_id, a.pdct_ctlg_id, a.product_name, a.billing_system, a.billing_code, a.fulfill_system, a.fulfill_code, a.speed_code, a.product_type, a.product_type_value, a.return_auth_applies, a.return_auth_term, a.nrc_waiver, a.nrc_waiver_product_id, a.linked_nrc_product_id, a.national_east_west, a.lata_list, a.state_list, a.product_info_url, a.isp_prov_flags, a.product_coefficient, a.last_updated, a.product_keywords, a.combo_product_list, a.additional_info, a.usoc_info, a.min_billing_days, a.data_nrf_product_id, a.video_nrf_product_id, a.account_line_level, a.web_desc, a.orderguicd, a.prod_startdate, a.prod_enddate, b.pdct_ctlg_id, b.package_name, b.billing_code, b.category_id, b.standalone_cpe, b.standalone_truckroll, b.btn_billed, b.emp_discount, b.package_type, b.last_updated, b.pkg_detail_descr, b.grandfathered AS pkg_grandfathered,
CASE CONCAT_WS('', a.grandfathered, c.pkg_prod_grandfathered)
WHEN 'YY' THEN 'Y'
WHEN 'YN' THEN 'Y'
WHEN 'NY' THEN 'Y'
ELSE 'N'
END AS grandfathered
FROM ssp2_pcat.products AS a, ssp2_pcat.packages AS b, ssp2_pcat.package_products AS c
WHERE strpos(billcodelist, CONCAT_WS('', '|', a.billing_code, '|')) > 0
AND a.product_id = c.product_id AND c.package_id = b.package_id
AND c.start_date <= LOCALTIMESTAMP AND c.end_date >= LOCALTIMESTAMP
ORDER BY 1;
ELSE
RAISE USING hint = -20001, message = 'Product List and Billing Code Lists are empty.', detail = 'User-defined exception';
END IF;
END;
$BODY$;
ALTER FUNCTION ssp2_pcat.prod_package_list(text, text)
OWNER TO ssp2_pcat;
Please advise,
Use IN parameter to pass a cursor name. Do not forget to RETURN vref_cursor. Working example:
CREATE FUNCTION my_func(arg1 text, arg2 text, vref_cursor refcursor)
RETURNS refcursor AS $$
BEGIN
OPEN vref_cursor FOR SELECT generate_series(1,3);
RETURN vref_cursor;
END;
$$ LANGUAGE plpgsql;
BEGIN;
SELECT my_func('first arg', 'second arg', 'vref_cursor');
FETCH ALL IN vref_cursor;
COMMIT;
Related
How to select into multiple variables inside a trigger function?
This is what I'd like to achieve: CREATE FUNCTION f() RETURNS trigger AS $$ BEGIN SELECT COUNT(*) AS total_num, SUM(width) AS total_width FROM some_table WHERE foo = NEW.foo; IF total_num > 0 AND total_width > 100 THEN RAISE EXCEPTION 'this is bad'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; But it's not yet syntactically correct. I've read I first need to DECLARE the variables (in this case total_num and total_width) so I can use those and use SELECT INTO but I've seen examples with a single variable / SELECT statement only. What if I have more of them?
You can list multiple variables in the into part. And the declare section needs to come before the first begin: CREATE FUNCTION f() RETURNS trigger AS $$ declare total_num bigint; total_width bigint; BEGIN SELECT COUNT(*), SUM(width) into total_num, total_width FROM some_table WHERE foo = NEW.foo; IF total_num > 0 AND total_width > 100 THEN RAISE EXCEPTION 'this is bad'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql;
Edit: I'm not sure whether the emphasis here is on the use of variables or the actual IF. This is meant as an answer on the latter: You can do this without variables using HAVING and EXISTS. IF EXISTS (SELECT '' FROM some_table WHERE foo = new.foo HAVING count(*) > 0 AND sum(width) > 100) THEN RAISE EXCEPTION 'this is bad'; END IF;
I tried to execute a query and gave me an error and i can't understand why?
DROP FUNCTION IF EXISTS top_5(customers.customerid%TYPE, products.prod_id%TYPE, orderlines.quantity%TYPE) CASCADE; CREATE OR REPLACE FUNCTION top_5(c_id customers.customerid%TYPE, p_id products.prod_id%TYPE, quant orderlines.quantity%TYPE) RETURNS orders.orderid%TYPE AS $$ DECLARE top_prod CURSOR IS SELECT inv.prod_id FROM inventory AS inv, products AS prod WHERE inv.prod_id=prod.prod_id ORDER BY inv.quan_in_stock desc, inv.sales limit 5; ord_id orders.orderid%TYPE; ord_date orders.orderdate%TYPE:= current_date; ordln_id orderlines.orderlineid%TYPE:=1; BEGIN SELECT nova_orderid() INTO ord_id; INSERT INTO orders(orderid, orderdate,customerid,netamount,tax,totalamount) VALUES(ord_id,ord_date,c_id,0,0,0); PERFORM compra(c_id, p_id, 1::smallint, ord_id, ordln_id, ord_date); IF (p_id = top_prod) THEN UPDATE orders SET totalamount = totalamount - (totalamount*0.2) WHERE ord_id = (SELECT MAX(ord_id) FROM orders); END IF; END; $$ LANGUAGE plpgsql; I have the following code and when i try to execute this SELECT top_5(1,1,'2'); i have this error ERROR: operator does not exist: integer = refcursor LINE 1: SELECT (p_id = top_prod)
You need to get the 'prod_id' value from the cursor 'top_prod'. You cannot compare two types. Try this, DECLARE top_prod_id top_prod%ROWTYPE; BEGIN OPEN top_prod; LOOP FETCH top_prod INTO top_prod_id; EXIT WHEN top_prod %NOTFOUND; IF (p_id = top_prod_id) THEN UPDATE orders SET totalamount = totalamount - (totalamount*0.2) WHERE ord_id = (SELECT MAX(ord_id) FROM orders); END IF; END LOOP; CLOSE top_prod; END;
PostgreSQL log trigger optimalization
I spent a lot of time trying to optimize our pgsql log trigger which started to be a problem. I did huge progress (from 18min to 2.5min by inserting 3M rows) but I would like to know if some pgSql masters will be able to do it even better. CREATE OR REPLACE FUNCTION table_log_trig() RETURNS trigger AS $BODY$ DECLARE col TEXT; -- Single column name to save newVal TEXT; -- New value for column oldVal TEXT; -- Old value for column colLimit TEXT[]; -- Columns that should be logged BEGIN IF TG_ARGV[0] IS NOT NULL THEN -- Trigger specifies columns to log SELECT array_agg(unnest) FROM unnest(string_to_array(TG_ARGV[0], ',')) INTO colLimit; ELSE -- Trigger with no params. Log all columns SELECT array_agg(json_object_keys) FROM json_object_keys(row_to_json(NEW)) WHERE json_object_keys NOT IN ('id', 'created_at', 'updated_at') -- Exceptions INTO colLimit; END IF; -- Loop over columns that should be saved in log FOREACH col IN ARRAY colLimit LOOP -- INSERT & UPDATE EXECUTE 'SELECT ($1).' || col || '::text' INTO newVal USING NEW; -- UPDATE IF TG_OP = 'UPDATE' THEN EXECUTE 'SELECT ($1).' || col || '::text' INTO oldVal USING OLD; END iF; -- Add only new or changed data IF newVal != oldVal OR (oldVal IS NULL AND newVal IS NOT NULL) OR (oldVal IS NOT NULL AND newVal IS NULL) THEN INSERT INTO tab_logs (record_id, field_name, old_value, new_value, created_at, created_by, action) VALUES (NEW.id, col, oldVal, newVal, NOW(), 999, 'O'); END IF; END LOOP; RETURN NEW; END; $BODY$ LANGUAGE plpgsql VOLATILE COST 100;
row_to_json() returns both column names and values; you may as well make use of these values, rather than extracting them later via dynamic SQL. I haven't thoroughly tested this, let alone benchmarked it, but here's the gist of it: CREATE OR REPLACE FUNCTION table_log_trig() RETURNS trigger AS $$ DECLARE OldJson JSONB = NULL; BEGIN IF TG_OP <> 'INSERT' THEN OldJson := to_jsonb(old); END IF; INSERT INTO tab_logs (record_id, field_name, old_value, new_value, created_at, created_by, action) SELECT new.id, key, OldValues.value, NewValues.value, now(), 999, 'O' FROM jsonb_each(to_jsonb(new)) NewValues LEFT JOIN jsonb_each(OldJson) OldValues USING (key) WHERE ( (TG_ARGV[0] IS NULL AND key NOT IN ('id', 'created_at', 'updated_at')) OR (TG_ARGV[0] IS NOT NULL AND key = ANY(string_to_array(TG_ARGV[0], ','))) ) AND OldValues.value::text IS DISTINCT FROM NewValues.value::text; RETURN NULL; END $$ LANGUAGE plpgsql VOLATILE;
Syntax error while creating function in postgresql
I got a syntax error while creating a procedure in postgresql.Here I attached my code.I got a error syntax error near "Continue" create function patient_form_values() RETURNS void AS $$ begin DECLARE columnName varchar(200) ; DECLARE done boolean default true; DECLARE CONTINUE handler for not found set done = false; DECLARE cur1 cursor for select distinct COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'currentdiagnosis'; open cur1; read_loop : loop fetch from cur1 into columnName; if done then leave read_loop; end if; set #insertValues := concat('INSERT INTO patient_form_temp(patient_id, form_template_id, creator_id, created_date) SELECT c.patient_id as patient_id, 41 AS form_template_id, 2 AS creator_id, c.created_date AS created_date FROM currentdiagnosis c WHERE c.', columnName,' IS NOT NULL GROUP BY c.patient_id, c.created_date'); select #insertValues; prepare stmt from #insertValues; execute stmt; end loop; close cur1; end ; $$ LANGUAGE plpgsql
You are trying to use a MySQL (or other DB?) function in PostgreSQL. There is no concept of CONTINUE HANDLER in PostgreSQL, so you have to convert the function into PostgreSQL format.
drop FUNCTION if exists migratePartnerAdvertiser(); CREATE OR REPLACE FUNCTION migratePartnerAdvertiser() RETURNS int4 AS ' DECLARE r RECORD; BEGIN FOR r IN select distinct COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = ''currentdiagnosis'' and table_schema=''public'' LOOP EXECUTE concat(''INSERT INTO patient_form_temp(patient_id, form_template_id, creator_id, created_date) SELECT c.patient_id as patient_id, 41 AS form_template_id, 2 AS creator_id, c.reg_date AS created_date FROM currentdiagnosis c WHERE c.'' , r.column_name , '' IS NOT NULL GROUP BY c.patient_id, c.reg_date''); END LOOP; return 1; END; ' LANGUAGE plpgsql;
PostgreSQL triggers and exceptions
I'm trying to get my first ever trigger and function to work, but how I throw exceptions and return data right way? PostgreSQL 8.4.1 CREATE TABLE "SHIFTS" ( id integer NOT NULL, -- SERIAL added timestamp without time zone DEFAULT now() NOT NULL, starts timestamp without time zone NOT NULL, ends timestamp without time zone NOT NULL, employee_id integer, modified timestamp without time zone, status integer DEFAULT 1 NOT NULL, billid integer, CONSTRAINT "SHIFTS_check" CHECK ((starts < ends)) ); -- Check if given shift time overlaps with existing data CREATE OR REPLACE FUNCTION shift_overlaps (integer, timestamp, timestamp) RETURNS boolean AS $$ DECLARE _employeeid ALIAS FOR $1; _start ALIAS FOR $2; _end ALIAS FOR $3; BEGIN SELECT COUNT(id) AS c FROM "SHIFTS" WHERE employee_id = _employeeid AND status = 1 AND ( (starts BETWEEN _start AND _end) OR (ends BETWEEN _start AND _end) ) ; -- Return boolean RETURN (c > 0); END; $$ LANGUAGE plpgsql ; CREATE OR REPLACE FUNCTION check_shift() RETURNS trigger AS ' BEGIN -- Bill ID is set, do not allow update IF tg_op = "UPDATE" THEN IF old.billid IS NOT NULL THEN RAISE EXCEPTION "Shift is locked" END IF; END IF; -- Check for overlap IF tg_op = "INSERT" THEN IF new.employee_id IS NOT NULL THEN IF shift_overlaps(new.employee_id, new.starts, new.ends) THEN RAISE EXCEPTION "Given time overlaps with shifts" END IF; END IF; END IF; -- Check for overlap IF tg_op = "UPDATE" THEN IF (new.employee_id IS NOT NULL) AND (new.status = 1) THEN IF shift_overlaps(new.employee_id, new.starts, new.ends) THEN RAISE EXCEPTION "Given time overlaps with shifts" END IF; END IF; END IF; RETURN new; END ' LANGUAGE plpgsql ; -- Shift checker trigger CREATE TRIGGER check_shifts BEFORE INSERT OR UPDATE ON "SHIFTS" FOR EACH ROW EXECUTE PROCEDURE check_shift() ; shift_overlaps(): SQL error: ERROR: query has no destination for result data check_shift(): SQL error: ERROR: unrecognized exception condition "Shift is locked"
You've got an error here: SELECT COUNT(id) AS c FROM "SHIFTS" WHERE employee_id = _employeeid AND status = 1 AND ( (starts BETWEEN _start AND _end) OR (ends BETWEEN _start AND _end) ) ; Such a select in a plpgsql procedure has to be SELECT INTO... like this: DECLARE c INTEGER; BEGIN SELECT COUNT(id) INTO c FROM "SHIFTS" WHERE employee_id = _employeeid AND status = 1 AND ( (starts BETWEEN _start AND _end) OR (ends BETWEEN _start AND _end) ) ; RETURN (c > 0); END; And here you've got to have the semicolon at the end of the line: enter code here`RAISE EXCEPTION "Shift is locked";
Not sure what you're trying to find out. You're managing to raise your own exceptions, so that's good. I would expect that any error handling would be in the code that evokes this method. If you want to do something inside the procedure, you need an EXCEPTION section: [ <> ] [ DECLARE declarations ] BEGIN statements EXCEPTION WHEN condition [ OR condition ... ] THEN handler_statements [ WHEN condition [ OR condition ... ] THEN handler_statements ... ] END; But generally I would expect you'd handle it in the calling code.
You have to use SELECT INTO to get a value returned by a query DECLARE [...] c boolean; SELECT COUNT(id) INTO c FROM "SHIFTS" WHERE [...]