CREATE FUNCTION public.feerange (txamount decimal,amount decimal,latefeemax decimal)
RETURNS INTEGER $feerange$
DECLARE total INTEGER;
DECLARE amountsum DECIMAL;
BEGIN
amountsum= amount + latefeemax;
if txamount <= amountsum AND txamount >= amount
THEN total=1
else
total=0
RETURN total
END;
$feerange $ LANGUAGE plpgsql;
while creating this function I'm getting an error of if condition
but I don't think there anything wrong.
the error is I'm getting is
/* ERROR: syntax error at or near "if" LINE 1: if txamount <=
amountsum AND txamount >= amount
What is wrong with this? Help me to do this!
Thank you !!
You could greatly simplify that as
CREATE FUNCTION public.feerange (
txamount decimal,
amount decimal,
latefeemax decimal
) RETURNS integer
LANGUAGE sql AS
'SELECT CAST (txamount <= amount + latefeemax AND txamount >= amount AS integer)';
You forgot end if; and ; in few places
CREATE FUNCTION public.feerange (txamount decimal,amount decimal,latefeemax decimal)
RETURNS INTEGER
as
$$
DECLARE total INTEGER;
DECLARE amountsum DECIMAL;
BEGIN
amountsum:= amount + latefeemax;
if txamount <= amountsum AND txamount >= amount
THEN total:=1;
else
total = 0;
end if;
RETURN total;
END;
$$ LANGUAGE plpgsql;
Related
I am trying to write a PostgreSQL function in C.
My goal is finding minimum value of a list. So, my function will be executed like these:
SELECT min_to_max(val) FROM (VALUES(1),(2),(3)) x(val);
SELECT min_to_max(val) FROM my_table;
Here is my C code and I lost here. For example, there is a function called "PG_GETARG_INT32" to get integer values, but I don' t know how to get values from a table in order to process. Any idea?
#include "postgres.h"
#include "fmgr.h"
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(get_sum);
Datum
get_sum(PG_FUNCTION_ARGS)
{
ArrayType *v1,
bool isnull;
isnull = PG_ARGISNULL(0);
if (isnull)
ereport( ERROR,
( errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("The input cannot be empty")));
List a = PG_GETARR_SOMEFUNCTION_2_GET_LIST(0);
# for loop iteration to find min_val
# return min_val
}
Edited(2022.05.13 - below is edited part):
Thanks to #Laurenz Albe. I made some progress.
Yet, now I want to go further(No needs to be in C language. As Laurenz Albe stated, I am just taking small steps).
My functions and aggregates like below to find min and max:
CREATE or replace FUNCTION find_min_func(
state integer,
next integer
) RETURNS integer
LANGUAGE plpgsql
STRICT
AS $$
declare
min_val integer;
begin
if $1 <= $2 then min_val := $1;
elsif $2 <$1 then min_val := $2;
end if;
return min_val;
END;
$$;
CREATE or replace AGGREGATE find_min(integer)
(
SFUNC = find_min_func,
STYPE = integer
);
CREATE or replace FUNCTION find_max_func(
state integer,
next integer
) RETURNS integer
LANGUAGE plpgsql
STRICT
AS $$
declare
max_val integer;
begin
if $1 >= $2 then max_val := $1;
elsif $2 > $1 then max_val := $2;
end if;
return max_val;
END;
$$;
CREATE or replace AGGREGATE find_max(integer)
(
SFUNC = find_max_func, -- State function
STYPE = integer -- State type
);
They are working great but now I want to do something like
SELECT min_to_max(val) FROM (VALUES(1),(2),(3)) x(val);
Expected output:
1 -> 3
So, I just wrote a state function and aggregate pair like below(I know it is wrong):
CREATE or replace FUNCTION find_min_and_max_func(
state integer,
next integer
) RETURNS varchar
LANGUAGE plpgsql
STRICT
AS $$
declare
min_val integer;
max_val integer;
output varchar;
begin
if $1 <= $2 then min_val := $1; max_val := $2;
elsif $2 <$1 then min_val := $2; max_val := $1;
end if;
output = cast(min_val as varchar) || '->' || cast(max_val as varchar) ;
return output;
END;
$$;
CREATE or replace AGGREGATE find_min_and_max(integer)
(
SFUNC = find_min_and_max_func, -- State function
STYPE = varchar -- State type
);
It is wrong because state function is taking arguments as integer but returns(?) varchar, so it varying.
How can I arrange my state function here?
Thanks!
To create an aggregate function, you have to use CREATE AGGREGATE. You create that in SQL. What you may need to implement in C is the state transition function (SFUNC), perhaps also others, depending on the kind of aggregate you want to create.
The aggregate function does not have to read from the table; the PostgreSQL executor will feed it the data it needs.
If you start writing C functions, you should perhaps start with something simpler than aggregate functions.
With the help of #Laurenz Albe(thanks to him again), I found the solution. Also, I checked out:
https://hoverbear.org/blog/postgresql-aggregates-with-rust/
https://hashrocket.com/blog/posts/custom-aggregates-in-postgresql
Here is my solution:
CREATE or replace FUNCTION find_min_and_max_func(
state point,
next integer
) RETURNS point
LANGUAGE plpgsql
STRICT
AS $$
declare
min_val integer;
max_val integer;
begin
if state[0] <= next then min_val := state[0];
elsif next < state[0] then min_val := next;
end if;
if state[1] >= next then max_val := state[1];
elsif next > state[1] then max_val := next;
end if;
return point(min_val, max_val) ;
END;
$$;
CREATE or replace FUNCTION find_min_and_max_final_func(
state point
) RETURNS varchar
LANGUAGE plpgsql
STRICT
AS $$
begin
return cast(state[0] as varchar) || '->' || cast(state[1] as varchar) ;
END;
$$;
CREATE or replace AGGREGATE find_min_and_max(integer)
(
SFUNC = find_min_and_max_func, -- State function
STYPE = point, -- State type
FINALFUNC = find_min_and_max_final_func,
initcond = '(1231231232131,0)'
);
SELECT find_min_and_max(value) FROM UNNEST(ARRAY [1, 2, 3]) as value;
find_min_and_max
------------------
1->6
(1 row)
Thanks!
Hi everyone this is major chunk for me I have been trying lot but not getting success
there is data in db as [AH_TRAN_CV] of length 9 as Integer. and I want that in varchar with 9 char and 2 decimals.
I guess this whole code may be wrong as I don't have knowledge just used internet to get till here.. The sample is like
123D is +1234
678{ is +6780
468K is -4682
Digit Positive Negative
0 { }
1 A J
2 B K
3 C L
4 D M
5 E N
6 F O
7 G P
8 H Q
9 I R
So kindly help. Below is just part of code
Code :
Case When (substring(convert(varchar,[AH_TRAN_CV] * 0.0000001), 9, 1) = '4'
and ([AH_TRAN_CV] >= 0.00))
Then concat (substring(convert(varchar,[AH_TRAN_CV]*0.0000001), 3, 8), 'D')
end [AH_TRAN_CV]
This encoding isn't a numeric format that can be parsed to a number. It's an EBCDIC zoned decimal encoding found only in mainframes.
You can create a function that splits the string and generates the appropriate sign and last digit from the input string, eg :
create function ZonedToNumber(#somenum varchar(20))
returns varchar(20)
as
begin
declare #c char = right(#somenum,1);
declare #digit char = case when #c between 'A' and 'I' then (ascii(#c) -64)
when #c between 'J' and 'R' then (ascii(#c) -73)
when #c in ('{','}') then 0
else null end
declare #sign char = case when #c between 'A' and 'I' or #c = '{' then '+'
when #c between 'J' and 'R' or #c = '}' then '-'
else null end
RETURN CONCAT(#sign,left(#somenum,len(#somenum)-1) ,#digit)
end
Instead of performing any clever arithmetic, the function uses CASE and subtracts a base ASCII value for each case to generate the last digit and sign.
You can apply that function to a zoned decimal string to convert it to an actual number, eg: SELECT dbo.ZonedToNumber('1234') will return =1234. You can also use it in a query :
declare #t table (num varchar(20))
insert into #t
values
('123D'),
('123K'),
('123{'),
('123}')
select cast(dbo.ZonedToNumber(num) as int)
from #t x
This will return :
1234
-1232
1230
-1230
You should use this function only to convert zoned decimals to actual numbers when loading the data. Almost nobody (outside mainframes) uses this format anymore.
I am trying to round down the value to the nearest 50.
1-50 it should round down to below 00 and when its 51-rest then it should round down to 50
ex:
245 (until 1-49) its should round down to 200
258 (from 50-99)then it should round down to 250
I tried this,its wrking good but I need smething other than case statement
#ResultAmount = ROUND(#ResultAmount, -2, 1) +
CASE WHEN RIGHT(CONVERT(INT, FLOOR(#ResultAmount)), 2) IN (00, 50)
THEN RIGHT(CONVERT(INT, FLOOR(#ResultAmount)), 2)
WHEN RIGHT(CONVERT(INT, FLOOR(#ResultAmount)), 2) BETWEEN 1 AND 49
THEN 00
WHEN RIGHT(CONVERT(INT, FLOOR(#ResultAmount)), 2) BETWEEN 51 AND 99
THEN 50
END
Thanks in advance!!!
This is all you need
SELECT FLOOR(#ResultAmount / 50) * 50;
e.g below
declare #ResultAmount decimal(10,2) = 249;
SELECT FLOOR(#ResultAmount / 50) * 50;
SET #ResultAmount = 250;
SELECT FLOOR(#ResultAmount / 50) * 50;
SET #ResultAmount = 200;
SELECT FLOOR(#ResultAmount / 50) * 50;
SET #ResultAmount = 199;
SELECT FLOOR(#ResultAmount / 50) * 50;
It sounds like numbers 0-50 get rounded up to "50", but any number larger than that should just get rounded to the nearest 50. Something like the following should work:
(CASE WHEN f1/50 < 1 THEN 1 ELSE ceiling(f1/50) END) * 50 AS rounded_to_50
You can simply divide the number by 50, round then multiply by 50 again, eg:
select cast(round(#i/50.0,0)*50 as int)
This will return 500 if #i is 524 but 550 if #i is 525.
You can create a function to make this easier:
create function fn_Round_By(#input int,#divider float)
returns int
as
begin
RETURN (cast(round(#input/#divider,0)*#divider as int));
end
Again, select dbo.fn_Round_By(525,50) returns 550 andselect dbo.fn_Round_By(524,50)` returns 500.
If you want values less than 50 to round up to 50, you can use a simple CASE, eg:
create function fn_Round_By(#input int,#divider float)
returns int
as
begin
RETURN (
CASE
WHEN #input <=#divider then #divider
else cast(round(#input/#divider,0)*#divider as int)
END
);
end
Rounding down is performed by the FLOOR function so a function that rounded down to a specific interval would be:
create function fn_Floor_By(#input int,#divider float)
returns int
as
begin
RETURN (cast(FLOOR(#input/#divider)*#divider as int));
end
or, preserving the logic that rounds up anything under 50:
create function fn_Floor_By(#input int,#divider float)
returns int
as
begin
RETURN (
CASE
WHEN #input <=#divider then #divider
else cast(FLOOR(#input/#divider)*#divider as int)
END
);
end
You can calculate modulo 50 and use this to reduce the original value
DECLARE #ResultAmount int = 243
SELECT #ResultAmount - (#ResultAmount%50)
In SQL Server for calculating percentage I have a function like below:
CREATE FUNCTION [dbo].[fuGetPercentage] ( #part FLOAT, #total FLOAT )
RETURNS FLOAT
AS
BEGIN
DECLARE #Result FLOAT = 0, #Cent FLOAT = 100;
IF (isnull(#total, 0) != 0)
SET #Result = isnull(#part, 0) * #Cent / #total;
RETURN #Result
END
I wonder that is there any better alternative for that, with same checks and a better calculating percentage like below:
SELECT (CASE ISNULL(total, 0)
WHEN 0 THEN 0
ELSE ISNULL(part, 0) * 100 / total
END) as percentage
I want to use it directly after SELECT like above.
There is one issue with using functions such as ISNULL. The query will not use indexes in that case. If the beauty of the code isn't in the first place then you can do something like that:
SELECT
CASE WHEN total * part <> 0 /* will check that both total and part are not null and != 0*/
THEN part * 100 / total
ELSE 0
END AS percentage;
I use #DmitrijKultasev answer but now, I found that it has two problems:
Error on conversion because of the overflow of result of multiply.
Performance problem; because of that math multiply and its conversion.
So I change it to this:
SELECT
CASE
WHEN total <> 0 AND part <> 0 THEN -- This will return 0 for Null values
part * 100 / total
ELSE
0
END AS percentage;
I was creating a function that returns BIT, I tried to "Return #count < 1", that did not work, how to convert boolean to BIT in TSQL.
Thanks
You will need to have a conditional statement:
if #count < 1
return 1
else
return 0
Or you could use a CASE statement:
case
when #count < 1 then return 1
else return 0
end
Can count ever be negative? And count should be integer
So what you want is "1 if #COUNT = zero, zero otherwise"
RETURN 1 - SIGN(#COUNT)
Or a simple transmogrification of Shark's answer:
return case
when #Count < 1 then 1
else 0
end
Note that a CASE may have as many WHEN clauses as you need.
Trivia: Curiously, a BIT can be set to 'TRUE' or 'FALSE'. Yeah, quoted strings. Go figure.