I'm working with plpgsql functions, which are called sequentially, for example:
create function a() returns void
language plpgsql as
$$
declare
v_ret integer;
begin
v_ret := (SELECT * FROM b());
end;
$$;
create function b() returns integer
language plpgsql as
$$
declare
v_ret integer;
begin
v_ret := (SELECT * FROM c());
return v_ret;
end;
$$;
create function c() returns integer
language plpgsql as
$$
declare
v_ret integer;
pointer record;
v_ret_sum integer;
begin
for record in (select * from temp_table) LOOP
v_ret := (SELECT * FROM d());
v_ret_sum := v_ret_sum + v_ret;
end loop;
return v_ret_sum;
end;
$$;
They are running in parallel with sidekiq, being called from my ROR API.
My trouble is that my db CPU is going very high in this process. I'm using 5 threads, so 5 times the same function, but with different arguments, runs at the same time.
What's a better options? Just accept this high CPU ratio, or call a function, then b function, and then c function from my API?
Thanks!
Related
Create function get_car_Price(Price_from int, Price_to int)
returns int
language plpgsql
as
$$
Declare
Car_count integer;
Begin
select count(*)
into Car_count
from Car
where Car_price between Price_from and Price_to;
return Car_count;
End;
$$;
The translation is straighforward:
CREATE FUNCTION get_car_Price(PRICE_FROM int, PRICE_TO int)
RETURNS INT
AS 'SELECT COUNT(*) FROM CAR WHERE Car_price BETWEEN PRICE_FROM AND PRICE_TO';
Function call:
SELECT get_car_price(100,200);
-- 0
Related: Scalar SQL UDFs
I've two functions,
CREATE OR REPLACE FUNCTION function_a(input varchar)
RETURNS setof integer AS $$
BEGIN
RETURN QUERY
SELECT somecolumn FROM some_things WHERE a_column = input;
END;
$$ LANGUAGE PLpgSQL;
CREATE OR REPLACE FUNCTION function_b(inputs varchar[])
RETURNS setof integer AS $$
DECLARE
input varchar;
result integer[];
BEGIN
FOREACH input IN ARRAY inputs LOOP
result := result || ARRAY[function_a(input)];
END LOOP;
END;
$$ LANGUAGE PLpgSQL;
I am running it like,
SELECT function_b(ARRAY['a', 'b']);
The error,
ERROR: query "SELECT result || ARRAY[function_a(input)]" returned more than one row
CONTEXT: PL/pgSQL function function_b(character varying[]) line 7 at assignment
All I want to do is to run a function over an array. I've always used scripting languages like Ruby to do this kind of stuff instead of using SQL, but I'm trying to learn SQL as it is much faster to get results on the db console itself. I wish it wasn't so frustrating.
First, in SQL we mainly write queries. You can do the same with a simple query:
select somecolumn
from some_things
where a_column = any(array['a', 'b']);
If you need a function, it may be an SQL one:
create or replace function sql_function(inputs text[])
returns setof integer language sql as $$
select somecolumn
from some_things
where a_column = any(inputs);
$$;
SQL functions are simpler and usually faster than plpgsql ones.
Back to your function_b() - the array constructor should look like this:
ARRAY(SELECT function_a(input))
Note also that the function does not return anything. Because you aggregate results in an array, you should unnest it to return rows:
CREATE OR REPLACE FUNCTION function_b(inputs varchar[])
RETURNS setof integer AS $$
DECLARE
input varchar;
result integer[];
BEGIN
FOREACH input IN ARRAY inputs LOOP
result := result || ARRAY(SELECT function_a(input));
END LOOP;
RETURN QUERY SELECT unnest(result);
END;
$$ LANGUAGE PLpgSQL;
You don't need the first function at all. I believe you want to return a set of integers from the table for the given input. A function like this is all you need.
CREATE OR REPLACE FUNCTION function_b(inputs varchar[])
RETURNS setof integer AS $$
BEGIN
RETURN QUERY SELECT somecolumn
FROM some_things WHERE a_column = ANY ( inputs );
END;
$$ LANGUAGE PLpgSQL;
Demo
I have the following case of using PostgreSQL view. I have to dynamically calculated the value of view column depending of the value of another dynamically calculated view column. Here is simplified version of the code:
BEGIN;
CREATE TABLE test
(
id serial PRIMARY KEY,
value integer NOT NULL
);
INSERT INTO test VALUES
(1, 13),
(2, 42);
CREATE FUNCTION inc(value integer)
RETURNS integer AS
$BODY$
BEGIN
RETURN value + 1;
END;
$BODY$
LANGUAGE plpgsql;
CREATE FUNCTION double(id integer)
RETURNS integer AS
$BODY$
DECLARE
local_value integer;
BEGIN
SELECT value_1 INTO local_value
FROM test_view WHERE double.id = test_view.id;
RETURN 2 * local_value;
END;
$BODY$
LANGUAGE plpgsql;
CREATE VIEW test_view AS
SELECT *,
inc(test.value) AS value_1,
double(test.id) AS value_2
FROM test;
COMMIT;
But this code falls in infinite recursion because of the following statement in the second function.
SELECT value_1 INTO local_value
FROM test_view WHERE double.id = test_view.id;
The exact error is the following:
ERROR: stack depth limit exceeded
HINT: Increase the configuration parameter "max_stack_depth
(currently 2048kB), after ensuring the platform's stack depth limit is adequate.
CONTEXT: PL/pgSQL function inc(integer) line 3 at RETURN
SQL statement "SELECT value_1 FROM test_view WHERE double.id = test_view.id"
PL/pgSQL function double(integer) line 5 at SQL statement
The problem can easily be overcome by using second view. For example:
CREATE FUNCTION double(value integer)
RETURNS integer AS
$BODY$
BEGIN
RETURN 2 * value;
END;
$BODY$
LANGUAGE plpgsql;
CREATE VIEW test_view_1 AS
SELECT *,
inc(test.value) AS value_1
FROM test;
CREATE VIEW test_view_2 AS
SELECT *,
double(test_view_1.value_1) AS value_2
FROM test_view_1;
But I don't like this approach because it requires creating of second view. It not scales in the case I have n different values each depending of the previous one. Then I must have n different views.
Is it possible to solve the problem with only one view?
Why not?
CREATE FUNCTION inc(value integer)
RETURNS integer AS
$BODY$
BEGIN
RETURN value + 1;
END;
$BODY$
LANGUAGE plpgsql;
CREATE FUNCTION double(value integer)
RETURNS integer AS
$BODY$
BEGIN
RETURN 2 * value;
END;
$BODY$
LANGUAGE plpgsql;
CREATE VIEW test_view AS
SELECT *,double(value_1) AS value_2 FROM
(SELECT *,
inc(test.value) AS value_1
FROM test) x;
select * from test_view;
Did i miss something from OP?
You can do it in on step
CREATE OR REPLACE FUNCTION incdouble(value integer)
RETURNS RECORD AS
$BODY$
DECLARE
linc integer;
ret RECORD;
BEGIN
linc := value + 1;
SELECT linc,linc*2 INTO ret;
RETURN ret;
END;
$BODY$
LANGUAGE plpgsql ;
select * from test t,incdouble(t.value) as (i integer ,d integer)
I have a stored procedure writen in T-SQL and I want to make it for PostgreSQL but I'm not so familiar with PostgreSQL.
My stored procedure look like this:
CREATE PROCEDURE usp_insert_allocated_time
#fld_project_id INT,
#fld_allocated_time INT
AS
DECLARE #project int;
SET #project = #fld_project_id;
DECLARE #allocated int;
DECLARE #time int;
BEGIN
SET #time = (SELECT SUM(fld_allocated_time)
FROM dbo.tbl_project_timesheet
WHERE fld_project_id =#project)
SET #allocated = (SELECT fld_allocated_days FROM dbo.tbl_project where fld_id = #project);
IF #allocated > #time
BEGIN
INSERT into dbo.tbl_project_timesheet(fld_project_id,fld_allocated_time)
VALUES(#fld_project_id,#fld_allocated_time);
END
ELSE
PRINT 'Not OK';
END
And I have to do something like this, but on line 10 I get this error:
ERROR: invalid input syntax for integer: "293.00"
SQL state: 22P02
Context: PL/pgSQL function
"SA_PRJ".usp_add_timesheet_record_new(integer,integer,numeric,numeric,character varying,character varying) line 10 at assignment
CREATE OR REPLACE FUNCTION "SA_PRJ".usp_add_timesheet_record_new(p_uid integer, p_project_id integer, p_allocated_time numeric, p_achieved_time numeric, p_task_desc character varying, p_obs character varying)
RETURNS void AS
$BODY$
declare alloc_id integer;
declare project integer;
declare allocated integer;
declare allocated_time integer;
BEGIN
project := p_project_id;
allocated_time := (SELECT SUM(fld_allocated_time)
FROM "SD_PRJ".tbl_project_timesheet
WHERE fld_project_id = project);
allocated := (SELECT fld_allocated_days FROM "SD_PRJ".tbl_project where fld_id = project);
if not "SA_ADM".usp_check_permission(p_uid, 'SA_PRJ', 'usp_add_timesheet_record') then
raise exception 'User ID % no have the permission!', p_uid;
end if;
select fld_id into alloc_id from "SD_PRJ".tbl_project_allocation where fld_emp_id = p_uid and fld_project_id = p_project_id;
BEGIN
IF (allocated > allocated_time) 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)
VALUES (p_uid,p_project_id,coalesce(alloc_id,0), p_allocated_time, p_achieved_time,p_task_desc, p_obs);
ELSE
RAISE NOTICE 'Not OK!!';
END IF;
END;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
It's more complex version in PostgreSQL for what I want.
You don't really give enough information to try and fix your problem, but the error message is pretty descriptive. You are trying to put 293.00 into an integer. Here I can reproduce:
DO
$$
DECLARE
i INT;
BEGIN
i := 293.00;
RAISE NOTICE 'i=%', i;
END
$$;
This will raise:
ERROR: invalid input syntax for integer: "293.00"
SQL state: 22P02
Context: PL/pgSQL function inline_code_block line 6 at assignment
You need to change your variable to the same datatype as the data you are trying to assign to it. For example:
DO
$$
DECLARE
i NUMERIC(5, 2);
BEGIN
i := 293.00;
RAISE NOTICE 'i=%', i;
END
$$;
This works and outputs:
NOTICE: i=293.00
Query returned successfully with no result in 14 ms.
I need to convert a comma separated text into a set of records. I created this function
but I am not convinced that the best way:
CREATE OR REPLACE FUNCTION F_StringListToRecord(pStringList TEXT, pDelimiter VARCHAR(10)) RETURNS SETOF RECORD AS $$
DECLARE
vIndex INT;
arrSize INT;
arrValue TEXT[];
BEGIN
arrValue := STRING_TO_ARRAY(pStringList, pDelimiter);
arrSize := ARRAY_UPPER(arrValue, 1);
FOR vIndex IN 1..arrSize LOOP
RETURN QUERY SELECT arrValue[vIndex];
END LOOP;
END $$
LANGUAGE plpgsql;
Is there any other function similar to STRING_TO_ARRAY (perhaps STRING_TO_RECORD)?
In 8.4 you can use:
select * from unnest(string_to_array(my_string_here,delimiter)) as v(x);