How to create UDF in BigQuery? Routine name missing dataset - google-bigquery

The following is the definition of a working function I have in a postgres db:
create function my_function(price numeric, qty numeric, min_charge numeric, other_fee numeric) returns numeric
language plpgsql
as
$$
DECLARE
_charge numeric;
BEGIN
IF qty = 0 THEN
RETURN 0.00::numeric(19, 2);
END IF;
_charge := GREATEST(price * qty, min_charge)::numeric(19, 2);
RETURN (_charge + COALESCE(other_fee, 0.00))::numeric(19, 2);
END;
$$;
alter function my_function(numeric, numeric, numeric, numeric) owner to my_location;
But when I try the following in BigQuery:
create function my_function(price numeric, qty numeric, min_charge numeric, other_fee numeric) returns numeric
language plpgsql
as
"""
DECLARE
_charge numeric;
BEGIN
IF qty = 0 THEN
RETURN 0.00::numeric(19, 2);
END IF;
_charge := GREATEST(price * qty, min_charge)::numeric(19, 2);
RETURN (_charge + COALESCE(other_fee, 0.00))::numeric(19, 2);
END;
""";
I get the following error:
Routine name "my_function" missing dataset while no default dataset is set in the request.
What is the command/syntax I am missing? I don't see a lot of documentation with concrete sql on how to correct for this.
Update: So far I have the following BigQuery I think is on the right track but it still gives errors for floating point literal 0.00 and other incompatible syntaxes
create procedure my_schema.my_function(price numeric, qty numeric, min_charge numeric, other_fee numeric)
BEGIN
IF qty = 0 THEN
RETURN 0.00::numeric(19, 2);
END IF;
_charge := GREATEST(price * qty, min_charge)::numeric(19, 2);
RETURN (_charge + COALESCE(other_fee, 0.00))::numeric(19, 2);
END;

just use dataset.function_name or project.dataset.function_name notion
if you want to use BQ scripting - you should rather use procedure - see CREATE PROCEDURE statement. Scripting is not supported in BQ functions. so if your logic involves scripting - proc is your option to go with

A sample for Parameterized User Defined Function is given below:
CREATE OR REPLACE FUNCTION `Project_Name.DataSet_Name.User_Defined_Function_Name`(Field Data_Type) AS
(
SELECT COALESCE(Feild, 0) AS Filed2
);

Related

PostgreSQL - Function with conditional local variable

I want to create a PostgreSQL function that will filter out table based on selected parameters.
However, I also need to perform some more complex logic based on the argument supplied. So I wanted to declare a local variable which would be based on some conditional logic.
What I have:
CREATE OR REPLACE FUNCTION get_something(parameter1 INT, parameter2 VARCHAR[])
DECLARE
conditional_variable := (
IF $1 = 50 THEN
'result-1'
ELSIF $1 = 100 THEN
ARRAY['result-2', 'result-3']
END IF;
)
RETURNS TABLE (
"time" BIGINT,
some_column NUMERIC
) AS $$
SELECT
time,
some_column
FROM "SomeNiceTable"
WHERE time = $1
AND some_dimension = ANY($2::VARCHAR[])
AND some_other_dimension = ANY(conditional_variable::VARCHAR[]);
$$ LANGUAGE SQL;
But it does not work this way. Is there a way how to achieve such thing?
You can not have DECLARE block and variables in a language sql function.
So you need to switch to language plpgsql and adjust the structure to be valid PL/pgSQL
CREATE OR REPLACE FUNCTION get_something(parameter1 INT, parameter2 VARCHAR[])
RETURNS TABLE ("time" BIGINT, some_column NUMERIC)
AS
$$
declare
conditional_variable text[];
begin
IF parameter1 = 50 THEN
conditional_variable := array['result-1'];
ELSIF parameter1 = 100 THEN
conditional_variable := ARRAY['result-2', 'result-3']
ELSE
????
END IF;
return query
SELECT
time,
some_column
FROM "SomeNiceTable"
WHERE time = $1
AND some_dimension = ANY($2::VARCHAR[])
AND some_other_dimension = ANY(conditional_variable);
END;
$$
LANGUAGE plpgsql;

Function postgreSQL with JSON parameters

I have to create a function that takes an object of this type :
"users":"John",
"Level":"1",
"list":[
{"id":"1", "price1":"100.50", "price2":"90.50"},
{"id":"2", "price1":"100.60", "price2":"80.50"},
{"id":"2", "price1":"105.50", "price2":"700.50"}
]}
For information JSON is not obliged but it is the thing that seemed the simplest to me. But maybe the creation of a POSTGRES type can work.
Then how to process the object if I use a JSON or PostGres Type.
CREATE OR REPLACE FUNCTION MYFUNCTION(myobject JSON ? CREATE TYPE ? )
RETURNS TABLE (
"OK" BOOLEAN,
"NB" VARCHAR
)
AS $$
DECLARE
BEGIN
-- I would like to get for each object in the list the price1 and price2 to
compare them.
END; $$
LANGUAGE 'plpgsql';
The crux of the question seems to be how to extract the values from the json object. This is one way:
select * from json_to_recordset('{
"users":"John",
"Level":"1",
"list":[
{"id":"1", "price1":"100.50", "price2":"90.50"},
{"id":"2", "price1":"100.60", "price2":"80.50"},
{"id":"2", "price1":"105.50", "price2":"700.50"}
]}'::json->'list') as foo(id int, price1 numeric, price2 numeric);
With a json variable instead of the literal string:
select * from json_to_recordset(jsonvariable->'list') as foo(id int, price1 numeric, price2 numeric)
Note. The json object you provide isn't legal. Some commas missing. I'd also suggest you use jsonb instead of json.
Edited:This is a skeleton on how you can use this in a plpgsql function:
create or replace function func(jsonvariable json) returns table (ok boolean, nb text) as
$BODY$
declare
r record;
begin
for r in (select * from json_to_recordset(jsonvariable->'list') as foo(id int, price1 numeric, price2 numeric)) loop
--- DO THINGS
ok:=(r.price1>r.price2);
nb:='this is text returned';
return next;
end loop;
end;
$BODY$
language plpgsql;
No Need to use loop for processing on the JSON ARRAY. you can use JSONB_ARRAY_ELEMENTS or JSONB_TO_RECORDSET for your requirement like below:
Using JSONB_TO_RECORDSET
CREATE OR REPLACE FUNCTION MYFUNCTION(myobject JSONB)
RETURNS TABLE (ok BOOLEAN, nb VARCHAR)
AS
$$
BEGIN
return query
select
price1>price2, -- you can change the condition here
id::varchar -- you can change the value as per your requirement
from jsonb_to_recordset(myobject ->'list') as x(id int, price1 numeric, price2 numeric);
END;
$$
LANGUAGE 'plpgsql';
Using JSONB_ARRAY_ELEMENTS
CREATE OR REPLACE FUNCTION MYFUNCTION1(myobject JSONB)
RETURNS TABLE (ok BOOLEAN, nb VARCHAR)
AS
$$
BEGIN
return query
select
(x->>'price1')::numeric > (x->>'price2')::numeric, -- you can change the condition here
(x->>'id')::varchar -- you can change the value as per your requirement
from jsonb_array_elements(myobject ->'list') as x;
END;
$$
LANGUAGE 'plpgsql';
DEMO

How Do I use or properly return a value from my PostgreSQL function?

CREATE OR REPLACE FUNCTION data.first()
AS $BODY$
DECLARE
res numeric;
BEGIN
PERFORM data.second(5,3,4);
IF(res > 10)THEN
something
ELSEIF(res < 10)THEN
something else
END IF
END;
$BODY$;
=========================================
CREATE OR REPLACE FUNCTION data.second(
a numeric,
b numeric,
c numeric
OUT res numeric
)
RETURNS numeric
AS $BODY$
BEGIN
res = a + b;
END;
$BODY$;
How do I use res in the parent function?
don't specify both OUT and function returns:
t=# CREATE OR REPLACE FUNCTION data.second(
a numeric,
b numeric,
c numeric,
OUT res numeric
)
AS $BODY$
DECLARE
BEGIN
res = a + b;
END;
$BODY$ language plpgsql;
CREATE FUNCTION
if you want to use the return of function ,use select into VAR, perform will just execute function discarding its output:
t=# CREATE OR REPLACE FUNCTION data.first() returns text
AS $BODY$
DECLARE
res numeric;
BEGIN
SELECT data.second(5,3,4) INTO res;
IF(res > 5)THEN
raise info 'second returned %',res;
END IF;
RETURN 'here is the return';
END;
$BODY$ language plpgsql;
CREATE FUNCTION
finaly:
t=# select * from data.first();
INFO: second returned 8
first
--------------------
here is the return
(1 row)
https://www.postgresql.org/docs/current/static/plpgsql.html

return %ROWTYPE from function PostgreSQL

I've tried to do following:
CREATE TABLE mytable(id integer NOT NULL,
name character varying,
CONSTRAINT pk_table PRIMARY KEY (id));
CREATE OR REPLACE FUNCTION fnmytable(inout p_rec mytable)
RETURNS mytable AS
$BODY$
declare
begin
p_rec.id := 1;--sequence
INSERT INTO mytable(id,
name)
VALUES (p_rec.id,
p_rec.name);
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
do
$$
declare
r_rec mytable%rowtype;
begin
r_rec.name := 'Jorge';
perform fnmytable(r_rec);
raise notice 'OUT ID: %', r_rec.id;
end;
$$
NOTICE: OUT ID: "NULL"
How to return the value of the sequence?
You have to use
SELECT * FROM fnmytable(r_rec) INTO r_rec;
I know that INOUT seems to suggest that the input parameter gets modified, but that is not the case. (INOUT p_rec mytable) is a shorthand for (p_rec mytable) RETURNS mytable.
Put differently, functions in PostgreSQL are always pass by value and not pass by reference.
Please try this
CREATE OR REPLACE FUNCTION fnmytable(inout p_rec mytable) AS
--EDITED HERE
$BODY$
declare
begin
p_rec.id := 1;--sequence
INSERT INTO mytable(id,
name)
VALUES (p_rec.id,
p_rec.name);
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
calling the function
do
$$
declare
r_rec mytable%rowtype;
begin
r_rec.name := 'Jorge';
select * from fnmytable(r_rec) into r_rec; --EDITED HERE
raise notice 'OUT ID: %', r_rec.id;
end;
$$

Cast Const Integer to Bigint in Postgres

I'm getting the below error while running the below script. My goal is to create a function in Postgres to return 1 as a bigint. Help please!
hashtagpostgresnoobie
ERROR: function result type must be bigint because of OUT parameters
CREATE OR REPLACE FUNCTION GetNumberOne(
OUT numberone bigint)
RETURNS SETOF record AS
$BODY$
SELECT CAST(1 AS BIGINT) AS "NUMBERONE";
$BODY$
LANGUAGE sql VOLATILE;
You've suddenly encountered the feature ) Record needs two and more fields. So when you have only one out variable, then result must be scalar.
So, you can simply do what compilers ask )
CREATE OR REPLACE FUNCTION GetNumberOne(
OUT numberone bigint)
RETURNS bigint AS
$BODY$
SELECT CAST(1 AS BIGINT) AS "NUMBERONE";
$BODY$
LANGUAGE sql VOLATILE;
plpgsql example:
CREATE OR REPLACE FUNCTION NumberOne()
RETURNS bigint AS
$BODY$
DECLARE num bigint;
BEGIN
num := 1;
RETURN num;
END
$BODY$
LANGUAGE plpgsql VOLATILE;
select * from NumberOne()