Proper procedure syntax for Postgres Function as Procedure in BigQuery? - google-bigquery

I have the following function in postgres:
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;
I'd like to convert this into BigQuery for the same functionality and so far I've tried:
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;
But I'm getting the following error:
Syntax error: Expected ";" but got floating point literal "0.00" at [4:16]
What is the proper command to produce the same postgres-style-function as a procedure successfully in BigQuery?

I was able to create the procedure in BigQuery by replacing the return data type to FLOAT64. Try this script:
CREATE PROCEDURE mydataset.my_function(price numeric, qty numeric, min_charge numeric, other_fee numeric)
BEGIN
IF qty = 0 THEN
SELECT 0.00 AS FLOAT64;
END IF;
SELECT (GREATEST(price * qty, min_charge) + COALESCE(other_fee, 0.00)) AS FLOAT64;
END;

I'd like to convert this into BigQuery for the same functionality
Consider below example
create temp function my_function(price numeric, qty numeric, min_charge numeric, other_fee numeric)
returns numeric as (
case when qty = 0 then 0
else greatest(price * qty, min_charge) + coalesce(other_fee, 0)
end
);
select my_function(1, 1, 1, 1)
if you want permanent function - just remove temp in create temp function

Related

How to create function in Vertca?

I began to study vertiсa. I tryis created customer function but getting error. Help me pliase
My code sql
create or REPLACE FUNCTION itog_report_status.get_warn_lvl(day_count NUMERIC)
returns varchar
AS $function$
DECLARE res varchar ;
BEGIN
select status::varchar
from itog_reports_status.status_warn_level
where days = (
select MIN(days)
from itog_reports_status.status_warn_level
where day_count <= days);
RETURN COALESCE(res, 'RED')
END;

Oracle sql to pgsql with table() function issue

I am doing the migration from Oracle to pgsql and I've the the oracle sql like below:
select code,product_no,qty qty from t_ma_tb_inventory
where code is not null and status=2
and update_type in (select * from table(splitstr(:types,',')))
and tb_shop_id=:shopId
order by update_time
and the splitstr function like below:
CREATE OR REPLACE FUNCTION splitstr (p_string text, p_delimiter text) RETURNS SETOF STR_SPLIT AS $body$
DECLARE
v_length bigint := LENGTH(p_string);
v_start bigint := 1;
v_index bigint;
BEGIN
WHILE(v_start <= v_length)
LOOP
v_index := INSTR(p_string, p_delimiter, v_start);
IF v_index = 0
THEN
RETURN NEXT SUBSTR(p_string, v_start);
v_start := v_length + 1;
ELSE
RETURN NEXT SUBSTR(p_string, v_start, v_index - v_start);
v_start := v_index + 1;
END IF;
END LOOP;
RETURN;
END
$body$
LANGUAGE PLPGSQL
SECURITY DEFINER
;
-- REVOKE ALL ON FUNCTION splitstr (p_string text, p_delimiter text) FROM PUBLIC;
can someone help me to write the equivalent code in pgsql?Thank you very much
You don't need to write your own function - Postgres already has a built-in function for that: string_to_array
select code, product_no, qty qty
from t_ma_tb_inventory
where code is not null and status=2
and update_type = any ( string_to_array(:types,',') )
and tb_shop_id = :shopId
order by update_time;
If you are passing a text, but you need to compare that to an integer, you need to cast the resulting array:
select code, product_no, qty qty
from t_ma_tb_inventory
where code is not null and status=2
and update_type = any ( string_to_array(:types,',')::int[] )
and tb_shop_id = :shopId
order by update_time;
However: that is a pretty ugly workaround that is only necessary in Oracle because it has no support for real arrays in SQL (only in PL/SQL).
In Postgres it would be much better to pass an array of integers directly (e.g. make :types an int[]). Then no parsing or casting would be necessary:
select code, product_no, qty qty
from t_ma_tb_inventory
where code is not null and status=2
and update_type = any ( :types )
and tb_shop_id = :shopId
order by update_time;

How to dynamically select into query

EXECUTE format(
'SELECT MAX(sum)
FROM table 1
WHERE %s = %s') INTO max_value USING id_column, id_value;
Getting error: ERROR: operator does not exist: character varying =
integer.
Also, when I try to run the below code:
update table1 set column2 = max_value + sum;
I get another error:
"ERROR: column "max_value" does not exist"
You can use this:
CREATE OR REPLACE FUNCTION maxvaloftable ()
RETURNS integer AS
$BODY$
DECLARE
resultat integer = 2;
BEGIN
resultat= (SELECT MAX(sum) FROM table) ;
RETURN resultat;
END;
$BODY$
LANGUAGE plpgsql VOLATILE STRICT
COST 100;
ALTER FUNCTION maxvaloftable()
OWNER TO postgres;
update table1 set column2 = (select maxvaloftable ()) + sum;

Error column reference is ambiguous

I have a function which:
compare two columns from different tables
make the insert
How can I get some improvement on this code because I get this error:
ERROR: column reference "fld_id" is ambiguous
SQL state: 42702
line 17 at PERFORM
CREATE OR REPLACE FUNCTION "SA_PRJ".usp_add_timesheet_test(p_uid integer, p_project_id integer, p_allocated_time numeric, p_achieved_time numeric, p_task_desc character varying, p_obs character varying, p_date timestamp without time zone)
RETURNS void AS
$BODY$
BEGIN
DECLARE sum_alloc_time numeric;
DECLARE alloc_hours integer;
DECLARE fld_id integer;
SELECT #sum_alloc_time = SUM(fld_allocated_time)
from "SD_PRJ".tbl_project_timesheet
where fld_project_id = p_project_id;
SELECT #alloc_hours = p.fld_allocated_days, #fld_id = p.fld_id
FROM "SD_PRJ".tbl_project p
INNER JOIN "SD_PRJ".tbl_project_timesheet t
ON p.fld_id=t.fld_id
where t.fld_project_id = p_project_id;
IF #sum_alloc_time <= #alloc_hours THEN
INSERT INTO "SD_PRJ".tbl_project_timesheet(fld_emp_id, fld_project_id, fld_is_allocated,fld_allocated_time, fld_achieved_time, fld_task_desc, fld_obs, fld_date)
VALUES (p_uid,p_project_id,coalesce(alloc_id,0), p_allocated_time, p_achieved_time,p_task_desc, p_obs, p_date);
RAISE NOTICE 'INSERT OK!';
ELSE
RAISE NOTICE 'NOT OK';
END IF;
END;
$BODY$ LANGUAGE plpgsql;
There are two tables:
"SD_PRJ".tbl_project (
fld_id
,fld_allocated_days)
"SD_PRJ".tbl_project_timesheet (
fld_id
, fld_project_id
,fld_allocated_time)
Working version:
CREATE OR REPLACE FUNCTION "SA_PRJ".usp_add_timesheet_test(
p_uid integer, p_project_id integer, p_allocated_time numeric
, p_achieved_time numeric, p_task_desc varchar, p_obs varchar, p_date timestamp)
RETURNS void AS
$func$
DECLARE
_sum_alloc_time numeric;
_alloc_hours integer;
_fld_id integer;
BEGIN
SELECT SUM(fld_allocated_time)
INTO _sum_alloc_time
FROM "SD_PRJ".tbl_project_timesheet
WHERE fld_project_id = p_project_id;
SELECT p.fld_allocated_days, p.fld_id
INTO _alloc_hours, _fld_id
FROM "SD_PRJ".tbl_project p
JOIN "SD_PRJ".tbl_project_timesheet t USING (fld_id)
WHERE t.fld_project_id = p_project_id;
IF _sum_alloc_time <= _alloc_hours THEN
INSERT INTO "SD_PRJ".tbl_project_timesheet
(fld_emp_id, fld_project_id, fld_is_allocated, fld_allocated_time
, fld_achieved_time, fld_task_desc, fld_obs, fld_date)
VALUES (p_uid, p_project_id, coalesce(alloc_id,0), p_allocated_time
, p_achieved_time, p_task_desc, p_obs, p_date);
-- alloc_id is undefined, you probably need to use SELECT .. FROM .. instead
RAISE NOTICE 'INSERT OK!';
ELSE
RAISE NOTICE 'NOT OK';
END IF;
END
$func$ LANGUAGE plpgsql;
But this is still needlessly inefficient. Could be done in a single DML statement with data-modifying CTEs. Try a search for related questions.
You need to study the basics first.

PL SQL how to select all columns

How do i make the following wrapper select all columns "*" instead of just if_type, and number_infected?
--spec
create or replace package WrapperSample is
type TResultRow is record(
if_type codes.cd%type
,number_infected Integer);
type TResultRowList is table of TResultRow;
function GetADedIcWarningsProv
(
p_hos_id in work_entity_data.hos_id%type
,p_date in date
) return TResultRowList
pipelined;
end WrapperSample;
/
--body
create or replace package body WrapperSample is
function GetADedIcWarningsProv
(
p_hos_id in work_entity_data.hos_id%type
,p_date in date
) return TResultRowList
pipelined is
v_refcur eOdatatypes_package.eOrefcur;
currentRow TResultRow;
begin
v_refcur := YourSchema.getADedIcWarningsProv(p_hos_id, p_date);
loop
fetch v_refcur
INTO currentRow;
exit when v_refcur%NotFound;
pipe row(currentRow);
end loop;
close v_refcur;
return;
end;
end WrapperSample;
/
I am not sure if I do understand your question and requirement.
But if you're looking for a way to get a table's content, or a portion thereof, this is probably how you would approach it:
create table tq84_test_table (
col_1 number,
col_2 varchar2(10),
col_3 date
);
insert into tq84_test_table values (1, 'one' , sysdate);
insert into tq84_test_table values (2, 'two' , sysdate+1);
insert into tq84_test_table values (3, 'three', sysdate-1);
create or replace package tq84_sss as
type record_t is table of tq84_test_table%rowtype;
function GetADedIcWarningsProv return record_t;
end;
/
create or replace package body tq84_sss as
function GetADedIcWarningsProv return record_t
is
ret record_t;
begin
select * bulk collect into ret
from tq84_test_table;
return ret;
end GetADedIcWarningsProv;
end;
/
You would then later use this function like so:
declare
table_content tq84_sss.record_t;
begin
table_content := tq84_sss.GetADedIcWarningsProv;
for i in 1 .. table_content.count loop
dbms_output.put_line(table_content(i).col_1 || ' ' ||
table_content(i).col_2 || ' ' ||
table_content(i).col_3
);
end loop;
end;
/
just use %rowtype
declare
...
someTableRow someTable%rowtype;
...
begin
select * into someTableRow from someTable where blah;
...