Error column reference is ambiguous - sql

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.

Related

Can't run "for loop" in PostgreSQL

I'm trying to create temp table and insert some data from LOOP.
I need to count all strings on each table, but i have error like this:
ERROR: ОШИБКА: переменная цикла по кортежам должна быть переменной типа запись или списком скалярных переменных
LINE 16: for r in (select "table_name", "name_campaign" from "Table_7...
^
SQL state: 42601
Character: 206
Table names all in one existing table.
do
$body$
declare
v_count integer;
begin
set enable_seqscan = off;
drop table if exists tmp_statyks;
create temp table tmp_statyks(table_name varchar(60), name varchar(60), x_count int);
for r in (select "table_name", "name_campaign" from "Table_7517829240")
loop
execute immediate 'select count(*) from ' || r."table_name"
into v_count;
INSERT INTO tmp_statyks("table_name", "name" , "x_count")
VALUES (r."table_name",r."name_campaign",v_count);
end loop;
end
$body$
language 'plpgsql';
select * from tmp_statyks
Just declare r as record:
declare
v_count integer;
r record;
begin

How to recursively return table in postgres function using plpgsql? Without using CTE/With Recursive

I tried implementing it here as follows
create or replace function getTextEditRecord(textId integer)
RETURNS Table (
text_id integer,
text_details character varying,
new_text_id integer) AS $$
DECLARE
curr_rec record;
temp_rec record;
BEGIN
curr_rec :=
(select tm.text_id, tm.text_details, tm.new_text_id from text_master tm
where tm.text_id = textId);
IF FOUND THEN
IF curr_rec.text_id != curr_rec.new_text_id THEN
temp_rec := getTextEditRecord(curr_rec.new_text_id);
--RETURN TABLE HERE
ELSE
-- No Recursive call directly return table
--RETURN TABLE HERE
END IF;
END IF;
--RETURN TABLE HERE
END;
$$ Language plpgsql;
Now I tried searching on google but could find that how to typecast record type to table type.
As in convert temp_rec to table type and return.
Doing this via a recursive function is the most inefficient way of doing it.
But anyhow, you can do something like this:
create or replace function get_text_edit_record(p_text_id integer)
RETURNS Table (
text_id integer,
text_details varchar,
new_text_id integer)
AS $$
DECLARE
curr_rec record;
BEGIN
select tm.text_id, tm.text_details, tm.new_text_id
into curr_rec
from text_master tm
where tm.text_id = p_text_id;
IF FOUND THEN
return query
select curr_rec.text_id, curr_rec.text_details, curr_rec.new_text_id;
--- IS DISTINCT FROM properly deals with NULL values
IF curr_rec.text_id IS DISTINCT FROM curr_rec.new_text_id THEN
return query
select *
from get_text_edit_record(curr_rec.new_text_id);
END IF;
END IF;
END;
$$ Language plpgsql;

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;

Race Condition between SELECT and INSERT for multiple columns

Note: This is a question which is a follow up of this solution. You need to read the link to get context for this question. Also, this is for postgres v9.4
If we want to return multiple columns now instead of just 1 column, how can we achieve it?
Let's take a table t:
create table t(tag_id int, tag text unique);
Now this is what I want:
whenever I call a method f_tag_id, I want it to return all the columns for the unique row if it exists in the table t else insert it and return all the columns.
So these are the things I tried for the f_insert_tag
Option1:
CREATE OR REPLACE FUNCTION f_insert_tag(tag_p_id int, _tag text)
RETURNS TABLE(_tag_p_id int, _tag_ text)
AS
$func$
BEGIN
INSERT INTO t(tag_id,tag) VALUES (tag_p_id, _tag);
return query Select * from t where t.tag_id = tag_p_id;
EXCEPTION WHEN UNIQUE_VIOLATION THEN -- catch exception, NULL is returned
END
$func$ LANGUAGE plpgsql;
Option 2:
CREATE OR REPLACE FUNCTION f_insert_tag(tag_p_id int, _tag text, out _tag_id int, out _tag_ text)
AS
$func$
BEGIN
INSERT INTO t(tag_id,tag) VALUES (tag_p_id, _tag);
Select t.tag_id, t.tag from t where t.tag_id = tag_p_id into _tag_id, _tag_;
EXCEPTION WHEN UNIQUE_VIOLATION THEN -- catch exception, NULL is returned
END
$func$ LANGUAGE plpgsql;
Option 3:
CREATE OR REPLACE FUNCTION f_insert_tag(tag_p_id int, _tag text)
returns setof t AS
$func$
BEGIN
INSERT INTO t(tag_id,tag) VALUES (tag_p_id, _tag);
return query Select * from t where t.tag_id = tag_p_id;
EXCEPTION WHEN UNIQUE_VIOLATION THEN -- catch exception, NULL is returned
END
$func$ LANGUAGE plpgsql;
All the 3 worked by themselves:
select f_insert_tag(1322, 'helloworldaa');
f_insert_tag
---------------------
(1322,helloworldaa)
For the other function, f_tag_id as well I tried many methods:
Option 1:
CREATE OR REPLACE FUNCTION f_tag_id(tag_p_id int, _tag text, out _tag_id int, out _tag_ text)
AS
$func$
BEGIN
LOOP
SELECT t.tag_id, t.tag FROM t WHERE t.tag = _tag
UNION ALL
SELECT f_insert_tag(tag_p_id, _tag)
into _tag_id, _tag_;
EXIT WHEN _tag_id IS NOT NULL; -- else keep looping
END LOOP;
END
$func$ LANGUAGE plpgsql;
Option 2:
CREATE OR REPLACE FUNCTION f_tag_id(tag_p_id int, _tag text)
RETURNS table(_tag_id int, _tag_ text) AS
$func$
BEGIN
LOOP
SELECT t.tag_id, t.tag FROM t WHERE t.tag = _tag
UNION ALL
SELECT f_insert_tag(tag_p_id, _tag)
into _tag_id, _tag_;
EXIT WHEN _tag_id IS NOT NULL; -- else keep looping
END LOOP;
END
$func$ LANGUAGE plpgsql;
For both these, I got the same error:
select f_tag_id(22, 'test');
ERROR: each UNION query must have the same number of columns
LINE 3: SELECT f_insert_tag(tag_p_id, _tag)
^
QUERY: SELECT t.tag_id, t.tag FROM t WHERE t.tag = _tag
UNION ALL
SELECT f_insert_tag(tag_p_id, _tag)
CONTEXT: PL/pgSQL function f_tag_id(integer,text) line 5 at SQL statement
The wrench in the works is SELECT f_insert_tag(tag_p_id, _tag) instead of
SELECT * FROM f_insert_tag(tag_p_id, _tag)
For Postgres 9.4
CREATE FUNCTION f_insert_tag(_tag_id int, _tag text, OUT _tag_id_ int, OUT _tag_ text)
AS
$func$
BEGIN
INSERT INTO t(tag_id, tag)
VALUES (_tag_id, _tag)
RETURNING t.tag_id, t.tag
INTO _tag_id_, _tag_;
EXCEPTION WHEN UNIQUE_VIOLATION THEN
-- catch exception, return NULL
END
$func$ LANGUAGE plpgsql;
CREATE FUNCTION f_tag_id(_tag_id int, _tag text, OUT _tag_id_ int, OUT _tag_ text) AS
$func$
BEGIN
LOOP
SELECT t.tag_id, t.tag
FROM t
WHERE t.tag = _tag
UNION ALL
SELECT * -- !!!
FROM f_insert_tag(_tag_id, _tag)
LIMIT 1
INTO _tag_id_, _tag_;
EXIT WHEN _tag_id_ IS NOT NULL; -- else keep looping
END LOOP;
END
$func$ LANGUAGE plpgsql;
db<>fiddle here
For Postgres 9.5 or later:
CREATE FUNCTION f_tag_id(_tag_id int, _tag text, OUT _tag_id_ int, OUT _tag_ text) AS
$func$
BEGIN
LOOP
SELECT t.tag_id, t.tag
FROM t
WHERE t.tag = _tag
INTO _tag_id_, _tag_;
EXIT WHEN FOUND;
INSERT INTO t (tag_id, tag)
VALUES (_tag_id, _tag)
ON CONFLICT (tag) DO NOTHING
RETURNING t.tag_id, t.tag
INTO _tag_id_, _tag_;
EXIT WHEN FOUND;
END LOOP;
END
$func$ LANGUAGE plpgsql;
db<>fiddle here
Basics here:
Is SELECT or INSERT in a function prone to race conditions?

SQL Query error insert into

I'm trying to do this function:
CREATE OR REPLACE FUNCTION "SA_PRJ".usp_verify_allocated_time(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$
declare alloc_id integer;
BEGIN
if not "SA_ADM".usp_check_permission(p_uid, 'SA_PRJ', 'usp_add_timesheet_record') then
raise exception 'User ID % nu are permisii pentru aceasta operatie!', p_uid;
end if;
SELECT *,
case when SUM(fld_allocated_time) / 24 < fld_allocated_days
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 'Not OK'
end as Alocated
from
(
SELECT p.fld_id, p.fld_allocated_days, t.fld_allocated_time
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
)AS Alias
GROUP BY fld_id, fld_allocated_days, fld_allocated_time
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION "SA_PRJ".usp_usp_verify_allocated_time(integer, integer, numeric, numeric, character varying, character varying, timestamp without time zone)
OWNER TO postgres;
I want to make a function to join two columns from tables "SD_PRJ".tbl_project and "SD_PRJ".tbl_project_timesheet and make the compare SUM(fld_allocated_time) / 24 < fld_allocated_days
from an input parameter p_project_id (WHERE t.fld_project_id = p_project_id) and make the insert if its ok or not.
The input column fld_project_id can have more than 1 appear in the table.
tbl_ = table name
fld_ = column / field name
"SD_PRJ" = my schema name
\ERROR: syntax error at or near "INTO"
Error:
LINE 12: INSERT INTO "SD_PRJ".tbl_project_timesheet(fld_emp_id, fl...
^
********** Error **********
ERROR: syntax error at or near "INTO" SQL state: 42601 Character: 550
Yeah, you're trying to write an insert inside a select statement.
What you probably want is something like this:
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)
select p_uid,p_project_id,coalesce(alloc_id,0), p_allocated_time, p_achieved_time,p_task_desc, p_obs
from
(
SELECT p.fld_id, p.fld_allocated_days, t.fld_allocated_time
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
)AS Alias
GROUP BY fld_id, fld_allocated_days, fld_allocated_time
having SUM(fld_allocated_time) / 24 < fld_allocated_days
Not completely sure if everything there is right, but that's the general gist of it I think.