SQL update with PL/sql function - sql

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.

Related

check if oracle function value given as a parameter exist in the table

My table looks like this
Teacher(ID, Name, Surname)
I want to give the user an output.
In case the id given as parameter to the function does not exist in the ID column in the table.
How can I handle this inside a function and give an output:
"The id asked is not present in the table"
Create or replace function subjects
(
code_value IN Teacher.ID % TYPE
)
RETURN NUMBER
IS
id_value NUMBER
BEGIN
SELECT id
INTO id_value
FROM TEACHER
WHERE ID = code_value
END
END subjects;
With a function you wrote, you can not - it returns a NUMBER while the result you want is a string (VARCHAR2).
There is a way - to write an exception handler which will be executed when select you wrote doesn't return anything as it'll raise NO_DATA_FOUND, so:
begin
select ...
exception
when no_data_found then
raise_application_error(-20000, 'The id asked is not present in the table');
end;
This will cause your program to stop (an error is raised, right?) so - if you want to move on, either
change return datatype or
switch to a procedure which will have an OUT parameter (or two - one for the ID, another for possible error message). Its (procedure's) drawback is that you can't use it in SELECT statements but you'll need a piece of PL/SQL code.

PL/pgSQL: Select where in is not working

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

Input table for PL/pgSQL function

I would like to use a plpgsql function with a table and several columns as input parameter. The idea is to split the table in chunks and do something with each part.
I tried the following function:
CREATE OR REPLACE FUNCTION my_func(Integer)
RETURNS SETOF my_part
AS $$
DECLARE
out my_part;
BEGIN
FOR i IN 0..$1 LOOP
FOR out IN
SELECT * FROM my_func2(SELECT * FROM table1 WHERE id = i)
LOOP
RETURN NEXT out;
END LOOP;
END LOOP;
RETURN;
END;
$$
LANGUAGE plpgsql;
my_func2() is the function that does some work on each smaller part.
CREATE or REPLACE FUNCTION my_func2(table1)
RETURNS SETOF my_part2 AS
$$
BEGIN
RETURN QUERY
SELECT * FROM table1;
END
$$
LANGUAGE plpgsql;
If I run:
SELECT * FROM my_func(99);
I guess I should receive the first 99 IDs processed for each id.
But it says there is an error for the following line:
SELECT * FROM my_func2(select * from table1 where id = i)
The error is:
The subquery is only allowed to return one column
Why does this happen? Is there an easy way to fix this?
There are multiple misconceptions here. Study the basics before you try advanced magic.
Postgres does not have "table variables". You can only pass 1 column or row at a time to a function. Use a temporary table or a refcursor (like commented by #Daniel) to pass a whole table. The syntax is invalid in multiple places, so it's unclear whether that's what you are actually trying.
Even if it is: it would probably be better to process one row at a time or rethink your approach and use a set-based operation (plain SQL) instead of passing cursors.
The data types my_part and my_part2 are undefined in your question. May be a shortcoming of the question or a problem in the test case.
You seem to expect that the table name table1 in the function body of my_func2() refers to the function parameter of the same (type!) name, but this is fundamentally wrong in at least two ways:
You can only pass values. A table name is an identifier, not a value. You would need to build a query string dynamically and execute it with EXECUTE in a plpgsql function. Try a search, many related answers her on SO. Then again, that may also not be what you wanted.
table1 in CREATE or REPLACE FUNCTION my_func2(table1) is a type name, not a parameter name. It means your function expects a value of the type table1. Obviously, you have a table of the same name, so it's supposed to be the associated row type.
The RETURN type of my_func2() must match what you actually return. Since you are returning SELECT * FROM table1, make that RETURNS SETOF table1.
It can just be a simple SQL function.
All of that put together:
CREATE or REPLACE FUNCTION my_func2(_row table1)
RETURNS SETOF table1 AS
'SELECT ($1).*' LANGUAGE sql;
Note the parentheses, which are essential for decomposing a row type. Per documentation:
The parentheses are required here to show that compositecol is a column name not a table name
But there is more ...
Don't use out as variable name, it's a keyword of the CREATE FUNCTION statement.
The syntax of your main query my_func() is more like psudo-code. Too much doesn't add up.
Proof of concept
Demo table:
CREATE TABLE table1(table1_id serial PRIMARY KEY, txt text);
INSERT INTO table1(txt) VALUES ('a'),('b'),('c'),('d'),('e'),('f'),('g');
Helper function:
CREATE or REPLACE FUNCTION my_func2(_row table1)
RETURNS SETOF table1 AS
'SELECT ($1).*' LANGUAGE sql;
Main function:
CREATE OR REPLACE FUNCTION my_func(int)
RETURNS SETOF table1 AS
$func$
DECLARE
rec table1;
BEGIN
FOR i IN 0..$1 LOOP
FOR rec IN
SELECT * FROM table1 WHERE table1_id = i
LOOP
RETURN QUERY
SELECT * FROM my_func2(rec);
END LOOP;
END LOOP;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM my_func(99);
SQL Fiddle.
But it's really just a a proof of concept. Nothing useful, yet.
As the error log is telling you.. you can return only one column in a subquery, so you have to change it to
SELECT my_func2(SELECT Specific_column_you_need FROM hasval WHERE wid = i)
a possible solution can be that you pass to funct2 the primary key of the table your funct2 needs and then you can obtain the whole table by making the SELECT * inside the function

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

How to get the objects from an oracle table of row objects

When dealing with an oracle table containing row objects I would expect that each row is an object and I can invoke functions on it or pass it to functions in any context.
As an example if I declare the following:
create type scd_type as object
(
valid_from date,
valid_to date,
member function get_new_valid_to return date
);
create type scd_type_table as table of scd_type;
create table scd_table of scd_type;
create procedure scd_proc (in_table in scd_type_table)
as
begin
... do stuff ...
end;
/
And now I try to call my proc with the table
begin
scd_proc (scd_table);
end;
/
I get an error. Even reading the rows into a nested table is not straight forward. I would expect it to work like this:
declare
temp_table scd_type_table;
begin
select * bulk collect into temp_table from scd_table;
... do stuff ...
end;
/
but instead I have to call the constructor for every line.
And last I cannot invoke functions in a merge statement even though it works in an update statement. Example:
update scd_table st
set st.valid_to = st.get_new_valid_to(); <--- Works.
merge into scd_table st
using (select sysdate as dateCol from dual) M
on (st.valid_from = M.dateCol)
when matched then update set st.valid_to = st.get_new_valid_to(); <--- Does not work.
So I guess there are three sub-questions here:
1) What is the easiest way to pass a table of row objects into a procedure expecting a nested table of the same type?
2) What is the easiest way to convert a table of row objects into a nested table of the same type?
3) Why can't I invoke functions on an object as part of a merge statement (but in an update statement)?
which all come down to the question of "How to extract objects from a table of row objects?".
I can't help but think you need to re-read the documentation on PL/SQL types.
You were close with the bulk collect code. Minor change given below:
declare
plsql_table scd_type_table;
begin
select VALUE(t) bulk collect into plsql_table from scd_table t;
-- do stuff
end;
/
I will admit, I have no idea why the merge fails but the update works.