I created this function that checks that total cost of a flight has been calculated correctly. It should multiply the price of seats e.g. 400 by the number of seats a customer has asked for e.g. 2. If the customer inputs the total correctly e.g.800 it should return the query successfully. If they don't input the total correctly e.g. 1000 then it should raise the exception 'price has been calculated incorrectly'.
CREATE FUNCTION check_totalcost() RETURNS trigger AS $check_totalcost$
DECLARE seatprice int;
DECLARE noofseats int;
BEGIN
SELECT priceperseat INTO seatprice
FROM flight
WHERE flightid = flightid;
SELECT numseats INTO noofseats
FROM flightbooking
WHERE flightid = flightid;
IF(seatprice*noofseats != new.totalcost) THEN
RAISE EXCEPTION 'price has been calculated incorrectly';
END IF;
RETURN NEW;
END;
$check_totalcost$ LANGUAGE plpgsql;
/* This is used to run the function above */
CREATE TRIGGER insert_totalcost
BEFORE INSERT OR UPDATE ON flightbooking
FOR EACH ROW EXECUTE PROCEDURE check_totalcost();
When I input the total incorrectly, it raises the exception which is what I want but when I input the total correctly, it also raises the exception when it should return the query successfully.
The queries
SELECT priceperseat INTO seatprice
FROM flight
WHERE flightid = flightid;
SELECT numseats INTO noofseats
FROM flightbooking
WHERE flightid = flightid;
make little sense as the conditions are always true.
I guess you want to get flightid from inserted/updated record. Use the special record NEW:
WHERE flightid = NEW.flightid;
You have one more logical error in your function. The number of seats also should be got from the new record.
CREATE OR REPLACE FUNCTION check_totalcost()
RETURNS trigger AS $check_totalcost$
DECLARE seatprice int;
BEGIN
SELECT priceperseat INTO seatprice
FROM flight
WHERE flightid = new.flightid;
IF seatprice * new.numseats != new.totalcost THEN
RAISE EXCEPTION 'price has been calculated incorrectly';
END IF;
RETURN NEW;
END;
$check_totalcost$ LANGUAGE plpgsql;
Related
I tried to use a cursor with multi parameters, the function was created without any problem, but then when I call my function using
select scratchpad.update_status()
I get the following error:
update is not allowed in a non volatile function
the function:
create function scrat.update_status() returns void
as
$$
DECLARE
day_to_process record;
BEGIN
FOR day_to_process in (SELECT distinct inst_status.date,inst_status.code,scrat.inst_status.id
FROM scrat.inst_status
WHERE inst_status.status ='S'
ORDER BY 1)
LOOP
raise notice 'Processing Date %', day_to_process.date::text;
update scrat.inst_status
set status = (select a.status from
(select status, max(date)
FROM scrat.inst_status
where status <> 'S'
and date::date < day_to_process.date
group by status
order by 2 desc
limit 1)a)
where inst_status.date = day_to_process.date
and id =day_to_process.id
and code=day_to_process.code;
END LOOP;
END ;
$$ LANGUAGE plpgsql STABLE;
As the documentation states:
STABLE indicates that the function cannot modify the database, and that within a single table scan it will consistently return the same result for the same argument values, but that its result could change across SQL statements.
So you will have to mark the function as VOLATILE.
I am using Postgresql11 and a function that works well in a single run fails when I add a LOOP statement with
"ERROR: query has no destination for result data HINT: If you want to
discard the results of a SELECT, use PERFORM instead."
The function has VOID as return value, selects data from a source table into a temp table, calculates some data and inserts the result into a target table. The temp table is then dropped and the function ends. I would like to repeat this procedure in defined intervals and have included a LOOP statement. With LOOP it does not insert into the target table and does not actually loop at all.
create function transfer_cs_regular_loop(trading_pair character varying) returns void
language plpgsql
as
$$
DECLARE
first_open decimal;
first_price decimal;
last_close decimal;
last_price decimal;
highest_price decimal;
lowest_price decimal;
trade_volume decimal;
n_trades int;
start_time bigint;
last_entry bigint;
counter int := 0;
time_frame int := 10;
BEGIN
WHILE counter < 100 LOOP
SELECT max(token_trades.trade_time) INTO last_entry FROM token_trades WHERE token_trades.trade_symbol = trading_pair;
RAISE NOTICE 'Latest Entry: %', last_entry;
start_time = last_entry - (60 * 1000);
RAISE NOTICE 'Start Time: %', start_time;
CREATE TEMP TABLE temp_table AS
SELECT * FROM token_trades where trade_symbol = trading_pair and trade_time > start_time;
SELECT temp_table.trade_time,temp_table.trade_price INTO first_open, first_price FROM temp_table ORDER BY temp_table.trade_time ASC FETCH FIRST 1 ROW ONLY;
SELECT temp_table.trade_time,temp_table.trade_price INTO last_close, last_price FROM temp_table ORDER BY temp_table.trade_time DESC FETCH FIRST 1 ROW ONLY;
SELECT max(temp_table.trade_price) INTO highest_price FROM temp_table;
SELECT min(temp_table.trade_price) INTO lowest_price FROM temp_table;
SELECT INTO trade_volume sum(temp_table.trade_quantity) FROM temp_table;
SELECT INTO n_trades count(*) FROM temp_table;
INSERT INTO candlestick_data_5min_test(open, high, low, close, open_time, close_time, volume, number_trades, trading_pair) VALUES (first_price, highest_price, lowest_price, last_price, first_open, last_close, trade_volume, n_trades, trading_pair);
DROP TABLE temp_table;
counter := counter + 1;
SELECT pg_sleep(time_frame);
RAISE NOTICE '**************************Counter: %', counter;
END LOOP;
END;
$$;
The error refers to the last SELECT statement in the function. If there is a SELECT without INTO it will always return results. When there's no LOOP this result will be used as the return value of the function (even if it is void).
When you add a LOOP there can't be any SELECT without INTO inside the loop because a single return value would be needed and now there will be many. In this case you need to use PERFORM which does exactly the same thing as a SELECT but discards the results.
Therefore change the last SELECT into a PERFORM and the error will go away:
PERFORM pg_sleep(time_frame);
I want a trigger is my database which changes the first zero of a phone number to the appropriate country-code (in my case it's +31). My code so far is:
create or replace function correct_number()
returns trigger as $$
declare
Pnumber integer;
begin
select substring(phone, 1, 1) from "user" where phone = new.phone into Pnumber;
if Pnumber = 0 then
update "user"
set phone = overlay("user".phone placing '+31' from 1 for 1)
where id = new.id;
return null;
end if;
end
$$ language plpgsql;
create trigger force_countrynr
before insert on "user"
for each statement execute procedure correct_number();
There problem is that every time I try to add data, my DBMS gives an error that the end of the procedure is reached without a return. This means obviously that it doesn't consider the if-statement to be true, but I can't figure out why.
This is the DDL of the attribute that my function concerns is a varchar(13) and the my testquery is as follows:
insert into gebruiker
values('U5', 'Hans', '12345', 'Hans', null, 'Kraay', 'Hansworst', '0000QW', 'beukenweg', '4', null, '0612345678', 'test#notnow.nl', '2015-12-12');
I've tried changing the datatype of the zero in my if-statement, changing the datatype of the number-variable and many things more, but I can't get it to work.
You want to correct the number in each inserted row, so the trigger should be for each row.
There is no reason to query the table in the trigger, as the currently inserted row is available in the new variable.
There is no reason to update the table, just modify the new record and return it.
create or replace function correct_number()
returns trigger as $$
begin
if left(new.phone, 1) = '0' then
new.phone:= '+31' || right(new.phone, -1);
end if;
return new;
end
$$ language plpgsql;
create trigger force_countrynr
before insert on "user"
for each row execute procedure correct_number();
I have a test table with three columns (file, qty, qty_total). I will input multiple rows like this for example, insert into test_table (file,qty) VALUS (A,5);. What i want is for on commit is for a trigger to take the value from qty and add it to qty_total. As what will happen is that this value will get updated as this example demonstrates. Update test_table set qty = 10 where file = A; So the qty_total is now 15. Thanks
Managed to solve this myself. I created a trigger function `CREATE FUNCTION public.qty_total()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100.0
VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
IF TG_OP = 'UPDATE' THEN
NEW."total" := (OLD.total + NEW.col2);
RETURN NEW;
ELSE
NEW."total" := NEW.col2;
RETURN NEW;
END IF;
END;
$BODY$;
ALTER FUNCTION public.qty_total()
OWNER TO postgres; This was called by a trigger CREATE TRIGGER qty_trigger
BEFORE INSERT OR UPDATE
ON public.test
FOR EACH ROW
EXECUTE PROCEDURE qty_total(); now when i insert a new code and value, the value is copied to the total, when it is updated, the value is added to the total and i have my new qty_total. This may not have the best error catching in it, but since i am passing the data from php, i am happy to make sure the errors are caught and removed.
I am not able to trigger the exceptions NO_DATA_FOUND from functions in PostgreSql 8.2 even if the returned rows or result sets is zero.
Here is my code;
CREATE OR REPLACE FUNCTION func_ex() RETURNS trigger AS
$func_ex$
DECLARE
var_name name;
BEGIN
Select empname INTO var_name from emp_table1 WHERE empid = 161232;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE EXCEPTION 'No data found';
RETURN NEW;
END;
return new
$func_ex$ LANGUAGE plpgsql;
-- End of Function
-- Creation of Trigger
CREATE TRIGGER insert_trigger1 AFTER update of empname
ON emp_table1 EXECUTE PROCEDURE func_ex();
-- insertion enteries.
INSERT INTO emp_table1 (empid, empname, salary) values (124, ' Sapmle_CustormerName', '3000');
To trigger NO_DATA_FOUND exception use:
Select empname INTO STRICT var_name from emp_table1 WHERE empid = 161232;
Details here: http://www.postgresql.org/docs/current/static/plpgsql-statements.html
If STRICT is not specified in the INTO clause, then target will be set
to the first row returned by the query, or to nulls if the query
returned no rows.
If the STRICT option is specified, the query must return exactly one
row or a run-time error will be reported, either NO_DATA_FOUND (no
rows) or TOO_MANY_ROWS (more than one row).