Wrong Calculation by User-Defined SQL-Function - sql

I have a function defined by
CREATE OR REPLACE FUNCTION public.div(dividend INTEGER, divisor INTEGER)
RETURNS INTEGER
LANGUAGE 'sql'
IMMUTABLE
LEAKPROOF
STRICT
SECURITY DEFINER
PARALLEL SAFE
AS $BODY$
SELECT ($1 + $2/2) / $2;
$BODY$;
It should calculate a commercial rounded result. Most of the times, it does the job. I don't know why, but select div(5, 3) gives me the correct answer while it doesn't when one parameter is calculated by an aggregate, e.g. select div(sum(val), 3) from (select 1 as val UNION SELECT 4) list is sufficient to trigger that.
How can I fix div? I don't want to cast every input.
BTW, using SELECT (cast($1 as integer) + cast($2 as integer)/2) / cast($2 as integer); as the definition of div didn't help.

Allow floats as parameters, then explicitly cast at the calculation, otherwise you have an implied conversion whilst passing the parameter.
CREATE OR REPLACE FUNCTION my_div(dividend FLOAT, divisor FLOAT)
RETURNS INTEGER
LANGUAGE 'sql'
IMMUTABLE
-- LEAKPROOF -- not allowed at dbfiddle.uk
STRICT
SECURITY DEFINER
PARALLEL SAFE
AS $BODY$
SELECT --($1 + $2/2) / $2;
(cast($1 as integer) + cast($2 as integer)/2) / cast($2 as integer)
$BODY$;
✓
select my_div(sum(val), 3)
from (select 1 as val UNION SELECT 4) x
| my_div |
| -----: |
| 2 |
dbfiddle here

Change the name of the function.
The function div(numeric, numeric) is a builtin Postgres function and there is an ambiguity which function you want to call:
select div(5, 3) -- calls your function public.div(integer, integer)
select div(5::bigint, 3) -- calls pg_catalog.div(numeric, numeric)
In the second case the arguments have to be resolved and the system function is chosen as first.
Note that the function sum(integer) gives bigint as a result.

Related

how to transform a number in binary representation to a Snowflake number

I have a number in binary (base-2) representation:
"10100110"
How can I transform it to a number in Snowflake?
Snowflake does not provide number or Integer to Binary function out of the box, however these UDF function can be used instead
I also overloaded the UDF in the event a string gets passed.
CREATE OR REPLACE FUNCTION int_to_binary(NUM VARIANT)
RETURNS string
LANGUAGE JAVASCRIPT
AS $$
return (NUM >>> 0).toString(2);
$$;
CREATE OR REPLACE FUNCTION int_to_binary(NUM STRING)
RETURNS string
LANGUAGE JAVASCRIPT
AS $$
return (NUM >>> 0).toString(2);
$$;
I tried with a SQL pure UDF - it worked at first, but not when using it with data over a table.
So I had to create a Javascript UDF:
create or replace function bin_str_to_number(a string)
returns float
language javascript
as
$$
return parseInt(A, 2)
$$
;
select bin_str_to_number('110');
For the record, this is the error I got when attempting a pure SQL UDF for the same:
SQL compilation error: Unsupported subquery type cannot be evaluated
The UDF:
create or replace function bin_str_to_number(a string)
returns number
as
$$
(select sum(value::number*pow(2,index))::number
from table(flatten(input=>split_string_to_char(reverse(a)))))
$$
That was a fun challenge for this morning! If you want to do it in pure SQL:
with binary_numbers as (
select column1 as binary_string
from (values('10100110'), ('101101101'), ('1010011010'), ('1011110')) tab
)
select
binary_string,
sum(to_number(tab.value) * pow(2, (tab.index - 1))) decimal_number
from
binary_numbers,
table(split_to_table(trim(replace(replace(reverse(binary_numbers.binary_string), '1', '1,'), '0', '0,' ), ','), ',')) tab
group by binary_string
Produces:
BINARY_STRING
DECIMAL_NUMBER
10100110
166
101101101
365
1010011010
666
1011110
94

How can wrap-on-overflow math be done in Postgres?

Given the following example function:
CREATE OR REPLACE FUNCTION add_max_value(_x BIGINT)
RETURNS BIGINT
LANGUAGE sql
AS $$
SELECT 9223372036854775807 + _x;
$$;
If this function is called with any positive value, the following error is returned:
SELECT add_max_value(1); -- Expecting -9223372036854775808 if math wrapped
-- SQL Error [22003]: ERROR: bigint out of range
How can I do wrap-on-overflow integer math in Postgres?
Please note:
I want to do this in the database, not in the application
I don't want it to promote to an arbitrary precision integer (NUMERIC)
Although the example only does addition, in practice I'm interested in other operations as well
As a SQL function there isn't a way. SQL functions cannot process exceptions. But a plpgsql function can:
CREATE OR REPLACE FUNCTION add_max_value(_x BIGINT)
RETURNS BIGINT
LANGUAGE plpgsql
AS $$
declare
bigx bigint;
begin
bigx = 9223372036854775807 + _x;
return bigx;
exception
when sqlstate '22003' then
return (9223372036854775807::numeric + _x - 2^64)::bigint;
end;
$$;
This is easily in my top five of the most wasteful SQL I have ever written:
create or replace function add_max_value(_x bigint)
returns bigint
language sql
as $$
with recursive inputs as (
select s.rn, r.a::int, s.b::int, (r.a::int + s.b::int) % 2 as sumbit,
(r.a::bit & s.b::bit)::int as carry
from regexp_split_to_table((9223372036854775807::bit(64))::text, '') with ordinality as r(a, rn)
join regexp_split_to_table((_x::bit(64))::text, '') with ordinality as s(b, rn)
on s.rn = r.rn
), addition as (
select rn, sumbit, sumbit as s2, carry, carry as upcarry
from inputs
where rn = 64
union
select i.rn, i.sumbit, (i.sumbit + a.upcarry) % 2, i.carry,
(i.carry::bit | a.upcarry::bit)::int
from addition a
join inputs i on i.rn = a.rn - 1
)
select (string_agg(s2::text, '' order by rn)::bit(64))::bigint
from addition
$$;

How to use mixed int and numeric arguments in a Postgres 9.1+ function

I'm looking for a way to create an icase() function which works with any second and third parameter compatible data types.
I tried in Postgres 9.4:
CREATE OR REPLACE FUNCTION public.icase(
cond1 boolean,
res1 anyelement,
conddefault anyelement)
RETURNS anyelement AS
' SELECT CASE WHEN $1 THEN $2 ELSE $3 END; '
LANGUAGE sql IMMUTABLE;
But:
select icase( true, 1.0, 0 )
causes error:
ERROR: function icase(boolean, numeric, integer) does not exist
LINE 9: select icase( true, 1.0, 0 )
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
How to fix this in 9.1+ so that second and third arguments can be either int or numeric?
This method may be called if both second and third parameters are text, char(n), date, numeric or int types.
The polymorphic types are strict in this moment - in other cases, PostgreSQL try to cast constants to most common type, but this step is missing for polymorphic types - so in this case, when you have described issue, you have to cast explicitly or you should not to use polymorphic types. Plan B is over function overloading.
CREATE OR REPLACE FUNCTION public.icase1(cond1 boolean,
res1 integer, conddefault integer)
RETURNS integer AS $$
SELECT CASE WHEN $1 THEN $2 ELSE $3 END;
$$ LANGUAGE sql;
CREATE OR REPLACE FUNCTION public.icase1(cond1 boolean,
res1 numeric, conddefault numeric)
RETURNS numeric AS $$
SELECT CASE WHEN $1 THEN $2 ELSE $3 END;
$$ LANGUAGE sql;
Then your code will work as expected:
postgres=> select icase1(true, 1.0, 0);
icase1
--------
1.0
(1 row)
postgres=> select icase1(true, 1.0, 1.0);
icase1
--------
1.0
(1 row)
postgres=> select icase1(true, 1, 0);
icase1
--------
1
(1 row)

Get individual columns from function returning SETOF <composite type> [duplicate]

This question already has answers here:
Postgres function returning table not returning data in columns
(2 answers)
Closed 8 years ago.
I created function like below:
CREATE TYPE points AS (
"gid" double precision
, "elevation" double precision
, "distance" double precision
, "x" double precision
, "y" double precision
);
CREATE OR REPLACE FUNCTION public.nd_test4()
RETURNS SETOF points AS $$
DECLARE
sql text;
rec points;
BEGIN
sql := 'select
"gid"
, "elev" as "elevation"
, st_distance(ST_MakePoint(1093147, 1905632) , "the_geom" ) as "distance"
, st_x("the_geom") as "x"
, st_y("the_geom")as "y"
from
"elevation-test"
where
st_within("the_geom" ,st_buffer(ST_MakePoint(1093147, 1905632), 15) )
order by distance limit 4';
FOR rec IN EXECUTE(sql) LOOP
RETURN NEXT rec;
END LOOP;
END;
$$ LANGUAGE plpgsql VOLATILE;
And when I run the function like select nd_test4();, I get a result with no filed names like below.
image?
How can I get result with filed name like this:
gid | elevation | distance | x | y
----+-----------+----------+---------+-------
1 | 350.0 | 10 | 12345.1 | 12435
Call the function with:
SELECT * FROM nd_test4();
Also, your function definition is needlessly convoluted. Simplify to:
CREATE OR REPLACE FUNCTION public.nd_test4()
RETURNS SETOF points AS
$func$
BEGIN
RETURN QUERY
SELECT gid
,elev -- AS elevation
,st_distance(ST_MakePoint(1093147, 1905632) , the_geom ) -- AS distance
,st_x(the_geom) -- AS x
,st_y(the_geom) -- AS y
FROM "elevation-test"
WHERE st_within(the_geom, st_buffer(ST_MakePoint(1093147, 1905632), 15))
ORDER BY distance
LIMIT 4;
END
$func$ LANGUAGE plpgsql;
Or better yet, use a plain SQL function here:
CREATE OR REPLACE FUNCTION public.nd_test4()
RETURNS SETOF points AS
$func$
SELECT gid
,elev -- AS elevation
,st_distance(ST_MakePoint(1093147, 1905632) , the_geom ) -- AS distance
,st_x(the_geom) -- AS x
,st_y(the_geom) -- AS y
FROM "elevation-test"
WHERE st_within(the_geom, st_buffer(ST_MakePoint(1093147, 1905632), 15))
ORDER BY distance
LIMIT 4
$func$ LANGUAGE sql;
No need for dynamic SQL.
I also stripped the gratuitous double quotes. Not needed for legal, lower-case identifiers. Exception is "elevation-test". You shouldn't use an operator (-) as part of a table name. That's just begging for trouble.
Aliases in the function body are replaced by column names of the composite type. They are only visible inside the function and therefore just documentation in your case.

How to calculate an exponential moving average on postgres?

I'm trying to implement an exponential moving average (EMA) on postgres, but as I check documentation and think about it the more I try the more confused I am.
The formula for EMA(x) is:
EMA(x1) = x1
EMA(xn) = α * xn + (1 - α) * EMA(xn-1)
It seems to be perfect for an aggregator, keeping the result of the last calculated element is exactly what has to be done here. However an aggregator produces one single result (as reduce, or fold) and here we need a list (a column) of results (as map). I have been checking how procedures and functions work, but AFAIK they produce one single output, not a column. I have seen plenty of procedures and functions, but I can't really figure out how does this interact with relational algebra, especially when doing something like this, an EMA.
I did not have luck searching the Internets so far. But the definition for an EMA is quite simple, I hope it is possible to translate this definition into something that works in postgres and is simple and efficient, because moving to NoSQL is going to be excessive in my context.
Thank you.
PD: here you can see an example:
https://docs.google.com/spreadsheet/ccc?key=0AvfclSzBscS6dDJCNWlrT3NYdDJxbkh3cGJ2S2V0cVE
You can define your own aggregate function and then use it with a window specification to get the aggregate output at each stage rather than a single value.
So an aggregate is a piece of state, and a transform function to modify that state for each row, and optionally a finalising function to convert the state to an output value. For a simple case like this, just a transform function should be sufficient.
create function ema_func(numeric, numeric) returns numeric
language plpgsql as $$
declare
alpha numeric := 0.5;
begin
-- uncomment the following line to see what the parameters mean
-- raise info 'ema_func: % %', $1, $2;
return case
when $1 is null then $2
else alpha * $2 + (1 - alpha) * $1
end;
end
$$;
create aggregate ema(basetype = numeric, sfunc = ema_func, stype = numeric);
which gives me:
steve#steve#[local] =# select x, ema(x, 0.1) over(w), ema(x, 0.2) over(w) from data window w as (order by n asc) limit 5;
x | ema | ema
-----------+---------------+---------------
44.988564 | 44.988564 | 44.988564
39.5634 | 44.4460476 | 43.9035312
38.605724 | 43.86201524 | 42.84396976
38.209646 | 43.296778316 | 41.917105008
44.541264 | 43.4212268844 | 42.4419368064
These numbers seem to match up to the spreadsheet you added to the question.
Also, you can define the function to pass alpha as a parameter from the statement:
create or replace function ema_func(state numeric, inval numeric, alpha numeric)
returns numeric
language plpgsql as $$
begin
return case
when state is null then inval
else alpha * inval + (1-alpha) * state
end;
end
$$;
create aggregate ema(numeric, numeric) (sfunc = ema_func, stype = numeric);
select x, ema(x, 0.5 /* alpha */) over (order by n asc) from data
Also, this function is actually so simple that it doesn't need to be in plpgsql at all, but can be just a sql function, although you can't refer to parameters by name in one of those:
create or replace function ema_func(state numeric, inval numeric, alpha numeric)
returns numeric
language sql as $$
select case
when $1 is null then $2
else $3 * $2 + (1-$3) * $1
end
$$;
This type of query can be solved with a recursive CTE - try:
with recursive cte as (
select n, x ema from my_table where n = 1
union all
select m.n, alpha * m.x + (1 - alpha) * cte.ema
from cte
join my_table m on cte.n = m.n - 1
cross join (select ? alpha) a)
select * from cte;
--$1 Stock code
--$2 exponential;
create or replace function fn_ema(text,numeric)
returns numeric as
$body$
declare
alpha numeric := 0.5;
var_r record;
result numeric:=0;
n int;
p1 numeric;
begin
alpha=2/(1+$2);
n=0;
for var_r in(select *
from stock_old_invest
where code=$1 order by stock_time desc)
loop
if n>0 then
result=result+(1-alpha)^n*var_r.price_now;
else
p1=var_r.price_now;
end if;
n=n+1;
end loop;
result=alpha*(result+p1);
return result;
end
$body$
language plpgsql volatile
cost 100;
alter function fn_ema(text,numeric)
owner to postgres;