How Do I use an Else If case in a Function Block - postgresql

I am trying to use a function to update a column on a table by creating a function that will do it.
I am trying to create a customer tier based on purchases made by each customer. I know the following code will update my column when I run the command by itself:
update email_campaign_detailed
set customer_tier = 'standard'
where num_dvd_rented <=20;
However I want to use a function to do this so I can input the data and have it auto-rank.
this is the code I'm using that isn't working:
Create or replace function customer_tier(num_dvds_rented bigint)
Returns character varying (50)
Language plpgsql
As
$$
Declare
Begin
case
when num_dvd_rented <=20 then update email_campaign_detailed set customer_tier ='standard'
when num_dvd_rented >= 21 and <= 39 then update email_campaign_detailed set customer_tier ='silver'
else update email_campaign_detailed set customer_tier ='gold'
End case;
end;
$$
I get a syntax error at the second "when" statement.
I cannot for the life of me figure out what I need to do.
Any help or insight is deeply appreciated.
Thanks!

You don't need a stored procedure when using the case-expression. Because case is an expression, you can just use it in your sql-statement (like any other expression):
update email_campaign_detailed
set
customer_tier = case
when num_dvd_rented <= 20 then 'standard'
when num_dvd_rented >= 21 and <= 39 then 'silver'
else 'gold'
end
where num_dvd_rented <= 20;
==== EDIT ===
Your stored procedure doesn't work because you can't have an expression (an update statement) within your when branches.
I suppose you want to use the num_dvds_rented parameter in the stored procedure for your where statement?
create or replace function customer_tier(num_dvds_rented bigint)
language plpgsql
As
$$
declare
begin
update email_campaign_detailed
set
customer_tier = case
when num_dvd_rented <= 20 then 'standard'
when num_dvd_rented >= 21 and <= 39 then 'silver'
else 'gold'
end
where num_dvd_rented <= num_dvds_rented;
end;
$$
Also, your stored procedure doesn't really return anything -- so you don't need the returns... in the declaration.
Now, if your goal is to do something like this:
update email_campaign_detailed
set customer_tier = customer_tier(num_dvd_rented)
where num_dvd_rented <= 20;
then your procedure might look like this:
create or replace function customer_tier(num_dvds_rented bigint)
returns character varying (50)
language plpgsql
As
$$
declare
begin
return case
when num_dvds_rented <= 20 then 'standard'
when num_dvds_rented >= 21 and <= 39 then 'silver'
else 'gold'
end;
end;
$$

Proof you can have expressions in the CASE branches. You just need to terminate then with a ;.
CREATE OR REPLACE FUNCTION public.case_test()
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
int_val integer;
BEGIN
CASE WHEN 1 = 1 THEN
select 1 into int_val;
ELSE
select 3 into int_val;
END CASE;
RAISE NOTICE 'Val is %', int_val;
END;
$function$
;
select case_test();
NOTICE: Val is 1
case_test
-----------

Related

PostgreSQL trigger function using "case when" and "else"

I made a code for update trigger. Whenever I update, I want to fill "price" column of table "new_book" with 1000 if "price" is blank, or 5% discount if "price" is not blank. My postsql code is
create function test()
returns trigger
as
$$
declare pr1 numeric ;
begin
case when old.price is null then pr1=1000
else pr1=pr1*0.95 end
end;
return new;
end
$$
language plpgsql;
but if i run this code, PGAdmin shows an error like that
ERROR: syntax error at or near "else"
LINE 8: else pr1=pr1*0.95 end
^
SQL state: 42601
Character: 117
How can I solve this problem?
You want an IF statement. And if you want to change the value that is stored in the table, you need to modify the new record:
create function test()
returns trigger
as
$$
begin
if new.price is null then
new.new_book := 1000;
else
new.new_book := new.price * 0.95
end if;
return new;
end
$$
language plpgsql;
In order for the assignment to actually be persisted, you have to use a BEFORE trigger:
create trigger set_new_book_trigger
before insert or update on the_table
for each row
execute procedure test();

Trigger PostgreSQL to update one value based on the sum of three others

I am trying to make a boolean become true when the sum of three integers is equal to 100 and false when the sum is not equal to 100.
I am making a trigger function in Postgres to do that, but getting the following error:
ERROR: «db_fondos.checksize» is not a known variable
LINE 6: DB_Fondos.CheckSize=true;
I hope you could help me.
Code:
CREATE FUNCTION check_sum()
RETURNS TRIGGER
AS $$
BEGIN
IF DB_Fondos.SizeLarge+DB_Fondos.SizeMid+DB_Fondos.SizeSmall=100 then
DB_Fondos.CheckSize=true;
END IF;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
Why not simply use a computed column for this instead of a trigger? The database manages the computation for you under the hood and you get an always up-to-date value when you query the table:
create table DB_Fondos (
...
SizeLarge int,
SizeMid int,
SizeSmall int,
check_sum boolean generated always as (
coalesce(SizeLarge, 0)
+ coalesce(SizeMid, 0)
+ coalesce(SizeSmall, 0) = 100
)
);
In a trigger, you relate to the newly inserted line with NEW, not with the table name:
CREATE FUNCTION check_sum()
RETURNS TRIGGER
AS $$
BEGIN
IF NEW.SizeLarge + NEW.SizeMid + NEW.SizeSmall = 100 THEN
NEW.CheckSize = true;
END IF;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;

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;

Example use of ASSERT with PostgreSQL

After reading the documentation for ASSERT, I am still confused how to use it, and can't find any examples online of how I would do something simple using ASSERT in a .sql script.
For example, say I want to ASSERT that the number of rows returned from SELECT * FROM my_table WHERE my_col = 3 is equal to 10.
Can someone provide a working example of that?
I would assume you try todo smth similar?
so=# select count(*) from pg_database;
count
-------
21
(1 row)
so=# do $$ begin assert (select count(*) from pg_database) = 21, 'not 21!';end;$$;
DO
so=# do $$ begin assert (select count(*) from pg_database) = 22, 'not 22!';end;$$;
ERROR: not 22!
CONTEXT: PL/pgSQL function inline_code_block line 1 at ASSERT
do $$
begin
ASSERT 1 = 2;
end;
$$ LANGUAGE plpgsql;
But note, it works only starting from PostgreSql 9.5. In older versions you can define your own assert-function such like this
CREATE OR REPLACE FUNCTION __assert(boolean) RETURNS VOID AS $$
BEGIN
IF NOT $1 THEN
RAISE EXCEPTION 'ASSERTING FAILED';
END IF;
END;
$$ LANGUAGE plpgsql;
And then use in this way
do $$
declare
tmp char;
begin
tmp := __assert(tmp_to_https('https') = 'https');
end;
$$ LANGUAGE plpgsql;

How to use the ELSIF statement in Postgres

I'm wondering if I can run the if statement by itself, or it can't stand on its own, has to be in a nested statement. I can't directly run the following code.
IF tax_year=2005 THEN
UPDATE table1 SET column1=column1*3;
ELSIF tax_year=2006 THEN
UPDATE table1 SET column1=column1*5;
ELSIF tax_year=2007 THEN
UPDATE table1 SET column1=column1*7;
END IF;
Also, I didn't write it out that when tax_year=2008, column1=column1. I'm not sure if it needs to be in the code since column1 won't change in 2008.
Thanks for your help!
IF / ELSIF / ELSE is part of PL/pgsql, which is an extension of pg, and it's enabled for new database by default.
You can create a function to wrap the IF statements. And call the function to execute these statements.
e.g
-- create function,
CREATE OR REPLACE FUNCTION fun_dummy_tmp(id_start integer, id_end integer) RETURNS setof dummy AS $$
DECLARE
BEGIN
IF id_start <= 0 THEN
id_start = 1;
END IF;
IF id_end < id_start THEN
id_end = id_start;
END IF;
return query execute 'select * from dummy where id between $1 and $2' using id_start,id_end;
return;
END;
$$ LANGUAGE plpgsql;
-- call function,
select * from fun_dummy_tmp(1, 4);
-- drop function,
DROP FUNCTION IF EXISTS fun_dummy_tmp(integer, integer);
And, there is a CASE statement, which might be a better choice for your requirement.
e.g
-- create function,
CREATE OR REPLACE FUNCTION fun_dummy_tmp(id integer) RETURNS varchar AS $$
DECLARE
msg varchar;
BEGIN
CASE id%2
WHEN 0 THEN
msg := 'even';
WHEN 1 THEN
msg := 'odd';
ELSE
msg := 'impossible';
END CASE;
return msg;
END;
$$ LANGUAGE plpgsql;
-- call function,
select * from fun_dummy_tmp(6);
-- drop function,
DROP FUNCTION IF EXISTS fun_dummy_tmp(integer);
You can refer to postgresql document for control statement for the details.
I did it with the following code:
-- UPDATE houston_real_acct_1single1property
-- SET convert_market_value=
-- CASE
-- WHEN tax_year=2005 THEN total_market_value*1.21320615034169
-- WHEN tax_year=2006 THEN total_market_value*1.17961794019934
-- WHEN tax_year=2007 THEN total_market_value*1.15884093604151
-- WHEN tax_year=2008 THEN total_market_value*1.12145267335906
-- WHEN tax_year=2009 THEN total_market_value*1.11834431349904
-- WHEN tax_year=2010 THEN total_market_value*1.0971664297633
-- WHEN tax_year=2011 THEN total_market_value*1.06256515125065
-- WHEN tax_year=2012 THEN total_market_value*1.04321957955664
-- WHEN tax_year=2013 THEN total_market_value*1.02632796014915
-- WHEN tax_year=2014 THEN total_market_value*0.998472101797389
-- WHEN tax_year=2015 THEN total_market_value
-- END;