PL/pgSQL: Select where in is not working - sql

I am currently using PL/pgSQL and I have a line of codes something like this:
FOR r_var in
select distinct value as val, count(*) as count from table where value IN (input) GROUP BY value;
LOOP
--do something here
END LOOP;
Input is from the user i.e. $$'A123','B456','C789'$$
I don't know why it wasn't working but if I manually put values instead of using the input, it is working.
UPDATE:
I discover where's the problem.
code must be:
FOR r_var in
EXECUTE 'select distinct value as val, count(*) as count from table where value IN ('||input||') GROUP BY value;
LOOP
--do something here
END LOOP;

Change the function to accept a text array:
select * from my_function(array['A123','B456','C789']::text)
and use = any in the where clause:
where value = any (input)
In general loops are a bad solution.
As suggested by #a_horse in the comments it is possible to keep the function and just convert the string to array:
where value = any (string_to_array(input, ','))

Passing comma seperated input value in stored procedure can be done by -
1) Adding the values in temporary table and selecting the values in procedure
2) Calling a function to split the comma seperated values by creating table valued function to parse
you can refer to this site-
http://www.codeproject.com/Articles/6083/Passing-comma-delimited-parameter-to-stored-proced

Related

Can we use LIKE operator along with MEMBER OF operator in a stored procedure?

I have an array of data using which I select rows from a table. For that I use member of operator in where clause. I want to know if we can do that same but by using Like operator along with member of operator.
When my Array consists of{Delhi, Mumbai, Kolkata}
I select the rows which have these three values in their row.
This is how I do that:
select ...
Into...
From xyz where city member of array;
///Receiving the array from an in parameter of the stored procedure.
And it works perfectly fine.
But If my array has {Del, Mum, Kolk} //parts of the actual names
How do I use this array for the same purpose, maybe using Like operator.
Create or replace zz2(ar in array_collection, c out sys_refcursor)
Is
anotherabc tablename.city%type
Begin
Open c
For
Select ABC
Into anotherabc
From tablename where city member of ar;
End zz2;
I expect the output to have all the rows which have cities starting with the alphabet/characters present in the array. Using member of operator
Something like this?
Select ABC
Into anotherabc a
From tablename WHERE EXISTS
( select 1 FROM ( select column_value as city
FROM TABLE(ar) ) s where a.city like s.city||'%' )
There is no direct way to use LIKE with MEMBER OF.
If It is the protocol that your collection contains the first three characters of the city name then you can use substr() to match only the first three characters in MEMBER OF.
try the following thing:
DECLARE
TYPE t_tab IS TABLE OF varchar(3);
l_tab1 t_tab := t_tab('Del','Mom','Kol');
BEGIN
DBMS_OUTPUT.put('Is ''Delhi'' MEMBER OF l_tab1? ');
IF SUBSTR('Delhi',1,3) MEMBER OF l_tab1 THEN -- note the use of SUBSTR here
DBMS_OUTPUT.put_line('TRUE');
ELSE
DBMS_OUTPUT.put_line('FALSE');
END IF;
END;
/
db<>fiddle demo
Cheers!!

How to select distinct in esql?

I have a subflow in esql (IBM Websphere Message Broker) where I need to achieve something similar to select distinct functionality.
Some background: I have a table in an Oracle database group_errcode_ref. This table is pretty much a fixed link/mapping of ERROR_CODE and ID. ERROR_CODE is unique, but ID can be duplicated. For example error code 4000 and 4001 can both be linked to ID 1.
In my esql subflow, I have an array of error codes that varies based on the current data coming into the flow.
So what I need to do is I need to take the input error code array, and select the ID for all the error codes in the array from my table group_errcode_ref
What I have now:
declare db rows;
set db.rows[] = (select d.ID from Database.group_errcode_ref as d where d.ERROR_CODE in (select D from errCodes.Code[] as D);
errCodes is the array of error codes from the input. row is an array of all IDs that correspond to the error codes.
This is fine, but I want to remove duplicates from the db.rows[] array.
I'm not certain the best way to do this in esql, but it does not support distinct. group by, or order by
If you are using the PASSTHRU statement, then all the functionality of your database manager is supported, so distinct as well.
The only thing you have to overcome is that you cannot directly mix database and messagetree queries in PASSTHRU, everything you pass to it goes directly to the database.
So your original solution would look something like this:
set db.rows[] = PASSTHRU 'select distinct d.ID from SCHEMA.group_errcode_ref as d where d.ERROR_CODE in ' || getErrorCodesFromInput(errCodes) TO Database.DSN1;
Here getErrorCodesFromInput is a function that returns character, which contains the error codes in your input, formatted correctly for the query, e.g. (ec1, ec2, ...)
My work around ended up not doing select distinct or sorting at all but another work around.
Basically I iterate through the entire array of ERROR_CODEs, then I query for the ID that corresponds to the error_code, then I select count(*) in a table I insert them to.
This works for my particular application only because I insert the ID/Issue pairs.
Basically it looks like:
for x as errs.Error[] do
declare db row;
set db.rows[] = passthru('select ID from my_static_map_table where error_code = ?;' values(x.Code));
declare db2 row;
set db2.rows[] = passthru('select count(*) from my_table_2 where guid = ? and id = ?;' values(guid, db.ID));
if db2.COUNT == 0 then
-- Here I do an insert into my_table_2 with ID and a few other values
end if;
end for;
Not really a proper answer, but it works for my specific application. Basically loop through every error code and select one at a time, rather than sending in the entire array. Then doing an insert into another database while avoiding duplicates by another select to see if it's already been inserted.
I'll still wait a week to see if there's a better answer and accept that one.
UPDATE
I've changed my code to match Attila's solution - which is much better and what I was looking for originally
Only thing I will add is my function that formats the error codes - which is really simple:
create function FlattenErrorCodesArray(in err row) returns char begin
declare idx int 1;
declare ret char;
for x as errs.Error[] do
if idx = 1 then
set ret = '(' || cast(x.Code as char);
else
set ret = ret || ',' || cast(x.Code as char);
end if;
set idx = idx + 1;
end for;
set ret = ret || ')';
end;

SQL update with PL/sql function

I have to update one column in my table ( the requirement to use function). I have created simple function for customer1 table. So how I can update my table using function . Is there any ways ?
CREATE OR REPLACE FUNCTION fn_easy (name_in in customer1.last_name%type)
RETURN customer1.first_name%type IS
name_tab customer1.first_name%type;
BEGIN
select first_name into name_tab from customer1
where last_name = name_in;
RETURN name_tab;
END fn_easy;
update customer1
set first_name = fn_easy(customer1.last_name);
I understand that we need to loop argument in function. The only one idea which I have is to do it through the cursor. But I don't think it is optimal to use cursor for this task when I have 200 records.
Your function is generally ok, but you have to ensure the select inside returns exactly 1 value, otherwise you will get an exception. If you want to select all values into an array, use BULK COLLECT INTO. To update a column value using a function, the column value type must be compatible with function return type.

Postgres run SQL statement from string

I would like to execute a SQL statement based on a string. I have created the SQL statement through a function which returns a string. Can anybody explain how I can execute the statement that is returned? I know that you can't do it in plain SQL, so I was thinking about putting it in a function. The only issue is that the columns in the statement aren't always the same, so I don't know which data types to use for the columns. I'm using Postgres 9.1.0.
For example, suppose the SQL string returned from my function the is:
Select open, closed, discarded from abc
But, it can also be:
Select open from abc
Or
Select open, closed from abc
How can I execute any of these strings, so that the results would be returned as a table with only the columns listed in the statement?
Edit: the function is written in PL/pgSQL. And the results will be used for reporting where they don't want to see columns that have no values. So the function that I wrote returns the names of all columns that have values and then add it to the SQL statement.
Thanks for your help!
I don't think you can return the rows directly from a function, because its return type would be unknown. Even if you specified the return type as RECORD, you'd have to list the returned columns at call time. Based on Wayne Conrad's idea, you could do this:
CREATE FUNCTION my_create(cmd TEXT) RETURNS VOID AS $$
BEGIN
EXECUTE 'CREATE TEMPORARY TABLE temp_result AS ' || cmd;
END;
$$ VOLATILE LANGUAGE plpgsql;
Then use the function like this:
BEGIN;
SELECT my_create(...);
SELECT * FROM temp_result;
ROLLBACK; -- or COMMIT

Sending in comma delimited values to WHERE clause in oracle stored procedure [duplicate]

This question already has answers here:
Oracle stored procedure with parameters for IN clause
(7 answers)
Closed 9 years ago.
I'm sending in a comma delimited parameter value to a stored procedure in Oracle. The parameter goes to a WHERE clause, I'm trying to achieve something like this:
valuestring = "ABC123, ABC456, ABC789"
SELECT *
FROM Tbl1
WHERE column IN (valuestring)
Problem is the select statement does not recognize the commas in between the values, it takes it as a single string so when the stored procedure is executed it returns no values.
Any help would be appreciated.
Ideally, rather than passing in a single string composed of comma-separated values, you would pass in a collection. If you have something like
CREATE TYPE value_tbl AS TABLE OF VARCHAR2(10);
then you could pass that collection into your procedure and use it in a WHERE clause
CREATE OR REPLACE PROCEDURE procedure_name( p_values IN value_tbl )
AS
...
BEGIN
FOR i IN (SELECT *
FROM table_name
WHERE column_name IN (SELECT *
FROM TABLE( p_values )))
LOOP
...
END LOOP;
END;
If you really need to pass in a comma-separated string, you can define the same collection type and then parse the string to populate the collection using something like Tom Kyte's str2tbl function. But that's going to be less efficient than passing in a collection to start with.
Can pass your value string like in given below way. This would convert entire comma separated string in different rows and hence will be easy to use where ever you want.
SELECT trim(x.column_value.extract('e/text()')) COLUMNS
from t t, table (xmlsequence(xmltype('<e><e>' || replace(valuestring,':','</e><e>')||
'</e></e>').extract('e/e'))) x );