Check constraint fire only on Insert and not on updates PostgresSQL - postgresql

My Check constraint is as follows:
ALTER TABLE tablename
ADD CONSTRAINT check_duplicate_rows
CHECK (reject_duplicate_rows(columnB, columnC, columnD) < 2);
I want the constraint to be evaluated only when you insert a record.
Currently it does for both the insert and update statements, The problem is that my system needs to update the inserted rows and the check constraint blocks the updates.
The reject_duplicate_rows function is as follows:
CREATE OR REPLACE FUNCTION reject_duplicate_rows(columnB integer, columnC integer, columnD integer)
RETURNS integer AS
$BODY$
DECLARE
results INTEGER := 1;
v_count INTEGER := 0;
BEGIN
IF columnC <> 23 THEN
RETURN results;
END IF;
SELECT total INTO v_count FROM
(SELECT columnB,
columnC,
columnD,
count(*) AS total
FROM table_name
WHERE B = columnB AND C = columnC AND D = columnD
GROUP BY 1, 2, 3)
as temp_table;
IF COALESCE(v_count, 0) = 0 THEN
RETURN results;
END IF;
IF v_count >= 1 THEN
results := 2;
END IF;
RETURN results;
EXCEPTION
WHEN OTHERS THEN
RETURN results;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 1000;
ALTER FUNCTION reject_duplicate_rows(integer, integer, integer)
OWNER TO postgres

Have you tried to create an UPDATE trigger? see Creating postgresql trigger

Related

How to compare two table value using if condition in function of Postgres

create or replace function trace.get_latest_exception_custom_msg(id varchar)
returns varchar
language plpgsql
as $$
declare
msg varchar ;
begin
perform t1.message, t1.created_time from table_1 t1 where t1.id = id order by t1.created_time desc limit 1;
perform t2.message, t2.created_time from table_2 t2 where t2.id = id order by t2.created_time desc limit 1;
if date(t1.created_time ) >= date(t2.created_time) then msg= t1.message;
elsif d date(t1.created_time ) < date(t2.created_time) then msg= t1.message;
else msg =t1.message;
end if;
return msg;
end;
while i call this function it give error ERROR: missing FROM-clause entry for table "t_1
You need to store the result of the two SELECT queries into variables in order to be able to be able to use them in an IF statement.
Your IF statement is also a bit confusing as all three parts assign the same value to msg. I assume that you want to use t2.message at least in one case.
create or replace function trace.get_latest_exception_custom_msg(p_id varchar)
returns varchar
language plpgsql
as
$$
declare
t1_msg varchar;
t1_created date;
t2_msg varchar;
t2_created date;
msg varchar;
begin
select t1.message, t1.created_time::date
into t1_msg, t1_created
from table_1 t1
where t1.id = p_id
order by t1.created_time desc
limit 1;
select t2.message, t2.created_time::date
into t2_msg, t2_created
from table_2 t2
where t2.id = p_id
order by t2.created_time desc
limit 1;
if t1_created >= t2_created then
msg := t1_msg;
elsif t1_created < t2_created then
msg := t2_msg; --<< ???
else
-- this can only happen if one (or both) of the DATEs is NULL.
msg := t1_msg;
end if;
return msg;
end;
$$

Function to select table rows based on certain conditions

create table my_table(x int, y int);
create function get_rows()
returns table (lie my_table) as $$
begin
-- 1 First try
--select * from my_table where x < 0;
-- 2 If empty then try
--select * from my_table where y < 0;
end; $$
language plpgsql;
How to implement a function that selects rows based on condition 1, but if such rows don't exists then select based on condition 2. If nether conditions can be satisfied then return an empty table.
You can us "IF EXISTS" to test the first condition. Then depending on the result use RETURN Query for the appropriate select;
create or replace function get_rows()
returns table (lie my_table)
language plpgsql
as $$
begin
-- 1 First try
if exists (select null from my_table where x < 0)
then
return query
select * from my_table
where x < 0;
-- 2 If empty then try
else
return query
select * from my_table
where y < 0;
end if;
end; $$;

Why iam getting this error while executing Postgres function

CREATE OR REPLACE FUNCTION public.flowrate7(
)
RETURNS TABLE(oms_id integer, flowrate numeric, chakno character varying)
LANGUAGE 'plpgsql'
COST 100
VOLATILE
ROWS 1000
AS $BODY$
declare temp_omsId integer;
declare temp_flowrate numeric(20,3);
declare temp_chakno varchar(100);
begin
DROP TABLE IF EXISTS tbl_oms;
DROP TABLE IF EXISTS tbl_calFlow;
CREATE temporary TABLE tbl_oms(omsid__ integer) ON COMMIT DELETE ROWS;
CREATE temporary TABLE tbl_calFlow(OmsId_ integer,FlowRate_ numeric(20,3),ChakNo_ varchar(100)) ON COMMIT DELETE ROWS;
insert into tbl_oms (select OmsId from MstOms);
while (select count(*) from tbl_oms) <> 0 LOOP
select temp_omsId = omsid__ from tbl_oms LIMIT 1;
temp_flowrate = (select (case when(InLetPressure > 0.5) then 1 else 0 end) from MstOms where OmsId = temp_omsId);
temp_chakno = (select ChakNo from MstOms where OmsId = temp_omsId);
insert into tbl_calFlow values (temp_omsId,temp_flowrate,temp_chakno);
delete from tbl_oms where omsid__ = temp_omsId;
END LOOP;
return query (select OmsId_,FlowRate_,ChakNo_ from tbl_calFlow);
end;
$BODY$;
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function flowrate7() line 19 at SQL statement
SQL state: 42601
You are retrieving values into the variables incorrectly. To store the value of a query result into a variable (or two) you need to use select .. into variable ...
CREATE OR REPLACE FUNCTION public.flowrate7()
RETURNS TABLE(oms_id integer, flowrate numeric, chakno character varying)
LANGUAGE plpgsql
AS $BODY$
declare
temp_omsId integer;
temp_flowrate numeric(20,3);
temp_chakno varchar(100);
begin
DROP TABLE IF EXISTS tbl_oms;
DROP TABLE IF EXISTS tbl_calFlow;
CREATE temporary TABLE tbl_oms(omsid__ integer) ON COMMIT DELETE ROWS;
CREATE temporary TABLE tbl_calFlow(OmsId_ integer,FlowRate_ numeric(20,3),ChakNo_ varchar(100)) ON COMMIT DELETE ROWS;
insert into tbl_oms
select OmsId
from MstOms;
while (select count(*) from tbl_oms) <> 0 LOOP
select omsid__
into temp_omsId --<< here
from tbl_oms LIMIT 1;
select case when inletpressure> 0.5 then 1 else 0 end, chakno
into temp_flowrate, temp_chakno --<< here
from MstOms
where omsid = temp_omsId;
insert into tbl_calFlow values (temp_omsId,temp_flowrate,temp_chakno);
delete from tbl_oms where omsid__ = temp_omsId;
END LOOP;
return query select omsid_, flowrate_, chakno_
from tbl_calflow;
end;
$BODY$;
However the processing of that function is unnecessarily complicated
if first copies all rows MstOms to tmp_MstOms
retrieve the ID for one row from tbl_oms
retrieve one row from MstOms calculating the flowrate
stores that one row in the temp table
deletes the just processed row from the (other) temp table
counts the number or rows in tbl_oms and if that is not zero moves on to the "next" row
This is an extremely inefficient and complicate way of calculating a simple value which will not scale well for large tables.
Doing a row-by-row processing of tables in the database is an anti-pattern to begin with (and doing that by deleting, inserting and counting rows is making that even slower).
This is not how things are done in SQL. The whole inefficient loop can be replaced with a single query which also allows you to change the whole thing to a SQL function.
CREATE OR REPLACE FUNCTION public.flowrate7()
RETURNS TABLE(oms_id integer, flowrate numeric, chakno character varying)
LANGUAGE sql
AS $BODY$
select omsid,
case when inletpressure> 0.5 then 1 else 0 end as flowrate,
chakno
from mstoms;
$BODY$;
Actually a view would be more appropriate for this.
create or replace view flowrate7
as
select omsid,
case when inletpressure> 0.5 then 1 else 0 end as flowrate,
chakno
from mstoms;

Postgresql: UPDATE before INSERT function

I have problem when create function for trigger. I want to UPDATE inserted value BEFORE INSERT data to DB.
My code look like this:
CREATE OR REPLACE FUNCTION test_func()
RETURNS TRIGGER AS
$$
DECLARE cnt INTEGER;
BEGIN
cnt := COUNT(*) FROM sample_tbl WHERE id = NEW.id AND created_date = NEW.created_date;
NEW.current_order := cnt + 1; // I want to set value of sample_tbl.current_order automatically
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER test_trigger
BEFORE INSERT
ON test_tbl
FOR EACH ROW
EXECUTE PROCEDURE test_func();
I inserted data then IDE said:
control reached end of trigger procedure without RETURN
Where: PL/pgSQL function test_func()
The error says that you must return something from the Trigger ( either NEW or NULL )
There's no Trigger needed for this. A simple View using this select query will give you the required result
--create or replace view sample_view as
select t.id, t.created_date,
row_number() OVER ( partition by id,created_date order by id ) as current_order
FROM sample_tbl t;
This will exactly match the records if updated using a Trigger
CREATE OR REPLACE FUNCTION test_func()
RETURNS TRIGGER AS
$$
DECLARE cnt INTEGER;
BEGIN
select COUNT(*) INTO cnt FROM sample_tbl WHERE id = NEW.id
AND created_date = NEW.created_date;
NEW.current_order := cnt + 1;
RETURN NEW; --required
END
$$ LANGUAGE plpgsql;
Demo
Your trigger function is just missing RETURN NEW; statement:
CREATE OR REPLACE FUNCTION test_func()
RETURNS TRIGGER AS
$$
DECLARE cnt INTEGER;
BEGIN
cnt := COUNT(*) FROM sample_tbl WHERE id = NEW.id AND created_date = NEW.created_date;
NEW.current_order := cnt + 1;
RETURN NEW;
END
$$ LANGUAGE plpgsql;

query has no destination for result data error

I am having the following function in `
CREATE OR REPLACE FUNCTION public.get_avalable_providers(
start_day_id integer,
end_day_id integer,
number_of_days integer,
requested integer)
RETURNS SETOF provider AS
$BODY$declare
required integer;
available_product integer;
p provider;
p_id integer;
noa integer;
begin
FOR p IN SELECT * FROM provider
loop
FOR p_id, noa IN SELECT id, number_of_availables FROM product
WHERE provider_id = p.id
LOOP
required = requested/noa;
select available_product =
public.get_available_products_biggerthan(
start_day_id, end_day_id, number_of_days, required, p_id);
if available_product = number_of_days then
return next p;
exit;
end if;
END LOOP;
end loop;
return;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION public.get_avalable_providers(integer, integer, integer,
integer, integer)
OWNER TO postgres;
The above functions is supposed to return some providers that have sufficient amount of the requested product in a days range
it is taking advantage anther function get_available_products_biggerthan and I am getting the following error:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function
get_avalable_products(integer,integer,integer,integer,integer) line 15 at
SQL statement
Question
where am I making mistake?
https://www.postgresql.org/docs/current/static/plpgsql-statements.html#PLPGSQL-STATEMENTS-SQL-ONEROW
The result of a SQL command yielding a single row (possibly of
multiple columns) can be assigned to a record variable, row-type
variable, or list of scalar variables. This is done by writing the
base SQL command and adding an INTO clause
try below (I only fixed obvious syntax):
CREATE OR REPLACE FUNCTION public.get_avalable_providers(
start_day_id integer,
end_day_id integer,
number_of_days integer,
requested integer)
RETURNS SETOF provider AS
$BODY$declare
required integer;
available_product integer;
p provider;
p_id integer;
noa integer;
begin
FOR p IN (SELECT * FROM provider)
loop
FOR p_id, noa IN (SELECT id, number_of_availables FROM product
WHERE provider_id = p.id)
LOOP
required = requested/noa;
select public.get_available_products_biggerthan(
start_day_id, end_day_id, number_of_days, required, p_id) INTO available_product ;
if available_product = number_of_days then
return next p;
exit;
end if;
END LOOP;
end loop;
return;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;