How do I call a declared array variable in a where clause in postgres? - arrays

I am trying to build a declared array from all the dogs that share the same family_id and query the dog_characteristics table using the array.
CREATE OR REPLACE FUNCTION update_dog_characteristics_guarantor_id()
RETURNS trigger AS $$
DECLARE dog_ids INT[];
BEGIN
SELECT id into dog_ids FROM dogs WHERE dogs.family_id = OLD.id;
IF ((OLD.family_id IS NOT NULL) && ((SELECT COUNT(*) FROM dog_ids) > 0)) THEN
UPDATE
dog_characteristics
SET
guarantor_id = NEW.guarantor_id
WHERE
dog_characteristics.account_id = OLD.account_id
AND dog_characteristics.dog_id IN ANY(dog_ids);
RETURN NULL;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
What I have tried
AND dog_characteristics.dog_id = ANY(dog_ids);
AND dog_characteristics.dog_id = ANY(dog_ids::int[]);
AND dog_characteristics.dog_id IN (dog_ids::int[]);
AND dog_characteristics.dog_id IN (dog_ids);
AND dog_characteristics.dog_id IN (ARRAY(dog_ids));
AND dog_characteristics.dog_id IN ARRAY(dog_ids);
AND dog_characteristics.dog_id IN implode( ',', dog_ids);
Most common error
ERROR: malformed array literal: "672"
DETAIL: Array value must start with "{" or dimension information.
CONTEXT: PL/pgSQL function update_dog_characteristics_guarantor_id() line 5 at SQL statement

There are multiple errors in your trigger function.
As dog_ids is declared as an array, the result of the first select has to be an array as well. To do that, you need to aggregate all IDs that are returned from the query.
So the first select statement should be
select array_agg(id) --<< aggregate all IDs into an array
into dog_ids
FROM dogs
WHERE dogs.family_id = OLD.id;
To check if an array has elements, you can't use select count(*), you need to use use array_length() or cardinality().
The && is not the "AND" operator in SQL - that's AND - so the if should be:
IF OLD.family_id IS NOT NULL AND cardinality(dog_ids) > 0 THEN
...
END IF;
The where condition on the array should be:
AND dog_characteristics.dog_id = ANY(dog_ids);

Related

Postgresql: Custom Type + Arrays combination

I'm unable to find where the issue is for the below program. the values of the custom type are displaying without any errors when I use RAISE NOTICE statements at the end. When I run the final select statement, the error is Array value must start with "{" or dimension information. Please help me with the select statement on how to call the package/function.
create
or
replace TYPE t_col_foo as object
(
ID NUMBER
, CLUSTERNAME VARCHAR2(300)
, "1200AM" varchar2(10));
create
or
replace TYPE T_COL_R AS TABLE OF t_col_foo;
CREATE OR REPLACE PACKAGE foo_avail_pkg
IS
FUNCTION foo_slots
(
p_ref_data anyarray
)
RETURN t_col_r[];
END foo_avail_pkg;
CREATE OR REPLACE PACKAGE BODY foo_avail_pkg
IS
FUNCTION foo_slots
(
p_ref_data anyarray
)
RETURN t_col_r[]
IS
-- declare
r_target_data t_col_foo:=t_col_foo(null,null,null);
r_target_data_1 t_col_foo;
r_source_data text[];
t_return t_col_tab1;
BEGIN
t_return:=t_col_tab1();
select
array
(
select
unnest( p_ref_data )
)
into r_source_data
;
-- r_target_data = '{}';
for i in coalesce(array_lower(r_source_data,1),0) .. coalesce(array_upper(r_source_data,1),0)
LOOP
r_target_data.ID := substr(r_source_data[i],1,instr(r_source_data[i],',',1,1)-1);
r_target_data.CLUSTERNAME := substr(r_source_data[i],length(r_target_data.ID)+2,(instr(r_source_data[i],',',length(r_target_data.ID)+1,2) - instr(r_source_data[i],',',1,1))-1);
r_target_data."1200AM" := 3;
r_target_data_1 :=row(r_target_data.ID ,r_target_data.CLUSTERNAME,r_target_data."1200AM") :: t_col_foo;
END LOOP;
-- dbms_output.put_line(r_target_data_1);
RETURN r_target_data_1;
end;
END foo_avail_pkg;
This is how I have to call
select * from foo_avail_pkg.foo_SLOTS(array
(
select
ID
||','
||CLUSTER_NAME
||','
||LOB
from
y limit 1
));
And the error is
ERROR: malformed array literal: "(1398,Sanity20feb,3)"
DETAIL: Array value must start with "{" or dimension information.

How to loop from an array received as a parameter in plpgsql function?

I'm kinda new into pgplsql and so far I have to create a function that loops an array that is received as a function.
The main idea of the function is to insert new records into a table that maps each id contained in the array received with a new formatted id, the format depends on the second parameter received and return the table "idsTable".
The problem is that when I try to create the function it sends me an error:
ERROR: loop variable of FOREACH must be a known variable or list of variables
LINE 38: FOREACH objectid IN ARRAY idsList LOOP
I'm not sure if I have to declare the objectid variable cause in the examples that I have seen they didn't.
So far I have this:
CREATE OR REPLACE FUNCTION createId(idsList varchar[], objectType varchar)
RETURNS TABLE(original_id varchar, new_id char) as
$$
BEGIN
IF LOWER(objectType) = 'global' THEN
FOREACH objectid IN ARRAY idsList LOOP
INSERT INTO idsTable(original_id, new_id)
VALUES(objectid, 'GID'||nextval('mapSquema.globalid')::TEXT);
END LOOP;
ELSE
FOREACH objectid IN ARRAY idsList LOOP
INSERT INTO idsTable(original_id, new_id)
VALUES(objectid, 'ORG'||nextval('mapSquema.globalid')::TEXT);
END LOOP;
END IF;
END;
$$ LANGUAGE plpgsql;
Any ideas of what could be wrong?
edit: I haven't add the part where the idsTable is returned.
Unrelated, but: you don't really need a loop for that. And you can simplify the function by only writing the INSERT once. You also forgot to return something from your function. As it is declared as returns table that is required:
CREATE OR REPLACE FUNCTION createid(idslist varchar[], objecttype varchar)
RETURNS TABLE(original_id varchar, new_id varchar) as
$$
declare
l_prefix text;
BEGIN
IF LOWER(objectType) = 'global' THEN
l_prefix := 'GID';
ELSE
l_prefix := 'ORG';
END IF;
RETURN QUERY --<< return the result of the insert
INSERT INTO idstable(original_id, new_id)
select t.x, l_prefix||nextval('mapSquema.globalid')::TEXT
from unnest(idslist) as t(x)
returning *
END;
$$ LANGUAGE plpgsql;

ORA-6504 Type of result set variables does not match

I am trying to print records that are mismatching (by some criteria) in my two tables that I have queried below in my procedure
CREATE OR REPLACE PROCEDURE one_two_mismatch( p_rc OUT SYS_REFCURSOR,
p_rc2 OUT SYS_REFCURSOR )
AS
BEGIN
dbms_output.put_line('T1 Table');
OPEN p_rc
FOR select t1.ENTITY_KEY, t1.ENTITY_ID, t1.COMPONENT_ID, t1.PARENT_KEY,
t1.ENTITY_TYPE_KEY from entity t1,(select entity_id from (select
c.entity_id, count(e.component_id) as ONE_CNT, count(c.component_id) as
TWO_CNT from entity e, entity_cmm c where e.entity_id =
c.entity_id group
by c.entity_id) where ONE_CNT <> TWO_CNT) t2 where t1.ENTITY_ID =
t2.ENTITY_ID;
dbms_output.put_line('T2 Table');
OPEN p_rc2
FOR select t3.ENTITY_KEY, t3.ENTITY_ID, t3.COMPONENT_ID, t3.PARENT_KEY,
t3.ENTITY_TYPE_KEY from entity_cmm t3,(select entity_id from
(select c.entity_id, count(e.component_id) as ONE_CNT,
count(c.component_id) as TWO_CNT from est_entity e, entity_cmm c
where e.entity_id = c.entity_id group by c.entity_id) where ONE_CNT <>
TWO_CNT)
t4 where t3.ENTITY_ID = t4.ENTITY_ID;
END one_two_mismatch;
I have written the following statements below in my PL/SQL developer SQL window to execute the above procedure but am encountering an error in line 9 which says type of result set variable or query doesn't match
declare
p_rc sys_refcursor;
p_rc2 sys_refcursor;
l_rec est_entity%rowtype;
m_rec est_entity_cmm%rowtype;
begin
one_two_MISMATCH(p_rc, p_rc2);
LOOP
FETCH p_rc INTO l_rec;
EXIT WHEN p_rc%NOTFOUND;
DBMS_OUTPUT.put_line(l_rec.ENTITY_KEY || ',' || l_rec.ENTITY_ID ||
',' || l_rec.COMPONENT_ID ||',' || l_rec.PARENT_KEY ||','||
l_rec.ENTITY_TYPE_KEY );
END LOOP;
CLOSE p_rc;
LOOP
FETCH p_rc2 INTO m_rec;
EXIT WHEN p_rc2%NOTFOUND;
DBMS_OUTPUT.put_line( m_rec.ENTITY_KEY || ',' || m_rec.ENTITY_ID || ',' ||
m_rec.COMPONENT_ID ||',' || m_rec.PARENT_KEY ||','||
m_rec.ENTITY_TYPE_KEY);
END LOOP;
CLOSE p_rc2;
end;
Can someone please help?
Based on the information provided, it sounds like one or both queries in procedure cmm_st_mismatch do not have the correct column list. In the anonymous block, the two variables declared to receive the ref cursor records
l_rec est_entity%rowtype;
m_rec est_entity_cmm%rowtype;
are declared as row types. The ref cursor being fetched from must have the same number, type and order of columns as the table referenced in the rowtype variable declaration. Judging from the error you described, there is a mismatch.

PostgreSQL full-text search with arrays

I would like to implement full-text search within my application but I'm running into some roadblocks associated with my Array-type columns. How would one implement a psql trigger so that the when my "object" table is updated, each element (which are strings) of its array column is added to the tsvector column of my "search" table?
In Postgres 9.6 array_to_tsvector was added.
If you are dealing with same table you can write it something like this.
CREATE FUNCTION tsv_trigger() RETURNS trigger AS $$
begin
IF (TG_OP = 'INSERT') OR old.array_column <> new.array_column THEN
new.tsv := array_to_tsvector( new.array_column);
END IF;
return new;
end
$$ LANGUAGE plpgsql;
CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE
ON my_table FOR EACH ROW EXECUTE PROCEDURE tsv_trigger();
If you are dealing with two tables than you need to write update
CREATE FUNCTION cross_tables_tsv_trigger() RETURNS trigger AS $$
begin
IF (TG_OP = 'INSERT') OR old.array_column <> new.array_column THEN
UPDATE search_table st
SET tsv = array_to_tsvector( new.array_column )
WHERE st.id = new.searchable_record_id
END IF;
# you can't return NULL because you'll break the chain
return new;
end
$$ LANGUAGE plpgsql;
Pay attention that it will differ from default to_tsvector( array_to_string() ) combination.
It goes without position numbers, and lowercase normalization so you can get a unexpected results.

Passing an array to function and use it in WHERE IN clause

I want to use array(which is passed to function) under Where in clause
Here is what i tried
First created the array type
create or replace type p_emp_arr as table of number
Function is
create or replace
FUNCTION getEmployee_func ( empId_arr IN p_emp_arr)
RETURN number IS
total number(2) := 0;
BEGIN
IF(empId_arr is null)
THEN
empIdClause := '';
ELSE
empIdClause := 'AND Employee.empId in (select column_value from table('||empId_arr||'))';
END IF;
....
RETURN total;
END;
But gives error
Error(17,23): PLS-00306: wrong number or types of arguments in call to '||'
The error is because CONCAT (||) operator accepts only scalar variables (string/number), you cannot pass an array to it.
You need to execute this as a dynamic PL/SQL block.
In case you want to bind the array dynamically, try something like this.
Bind the variables using IN and OUT keywords appropriately.
In your Anonymous block string, prefix the to-be-bind variables with colon (:)
EXECUTE IMMEDIATE '
BEGIN
SELECT
COLUM1,COLUMN2..
INTO
:VAR1, :VAR2..
FROM .... WHERE...
AND Employee.empId in (select column_value from table(:empId_arr));
END;
'
USING OUT VAR1, OUT VAR2... IN empId_arr;
It can also be Simply,
OPEN EMP_CURSOR FOR
'SELECT * FROM Employee
where empId in SELECT COLUMN_VALUE FROM TABLE(:empId_arr)'
USING empId_arr ;
If you take the output as a cursor;
AS Wernfried mentioned.. Using MEMBER OF operator.
OPEN EMP_CURSOR FOR
'SELECT * FROM Employee
where empId member of :empId_arr'
USING empId_arr ;

Resources