I have an assignment, and the course materials do not cover how to do this, and a web search has not been help regarding cursors within functions.
The assignment is to return all last names in the database, but change any according to the case statement parameters. And I am required to use a cursor. Here is my code:
create or replace function Convert_New_Name
return sys_refcursor
as
c_results sys_refcursor;
begin
open c_results for
select
case last_name
when 'King' then 'LION'
when 'Ford' then 'CAR'
when 'Miller' then 'BEER'
else last_name
end as new_name
from employees;
return c_results;
end convert_new_name;
I'm having a hard time finding anything pertinent in searches. The database has 20 rows. This code returns all 20 rows, with required changes, but it returns those 20 row 20 times, instead of once.
There was another post on here with almost the same assignment, but when I tried to post on there, it got deleted, and I got told to ask my own question.
The function code you posted is fine. The calling code should call function Convert_New_Name only once. If you plan to call it from a SQL statement, then this query should do the job:
Select Convert_New_Name() From dual;
Related
What I want to do is storing the result of a PLSQL Script in a variable of a Kettle Transformation.
The script is located in a table input step and basically creates a select statement for me and puts it in a varchar variable of that PLSQL Script.
DECLARE
Statement VARCHAR(2000);
BEGIN
-- Here happens some stuff
Statement := 'select * from Foo';
END;
I just can´t figure out how I can map the variable Statement to one that exists in the Kettle Transformation.
Any ideas?
After a couple days I was able to solve this.
What I haven´t realized when I wrote the question was that a mere PLSQL Script doesn´t return anything.
The solution is actually very simple:
Create a new Transformation in Spoon.
Create a PLSQL Function (which has a return value of course).
Inside the new Transformation create a step "Table Input" and there you can call the function like this:
select myFunction('$someParameter') AS Statement FROM dual
If you want to provide some parameters for the function like I did make sure to check "replace variables in script" in the Table Input Step.
Connect that Step with a "Set Variables" Step ("Copy Rows to Result" works as well) and in my case I added the field Statement to the step with a new Variable that is valid in the parent job.
i am trying to find out at what age an employee started working.
if he started under 16 he should report this 'Error when entering the date of birth' mistake. so my trigger is created but my trigger is not working
I get ever this error: ORA-01422: Exact retrieval returns more than the requested number of lines
i can't find the problem
Here is the code:
SET SERVEROUTPUT ON
ACCEPT Birthday PROMPT ' Pleas give you Date of birth: '
CREATE OR REPLACE TRIGGER T_Controll
before INSERT ON meine_Firma -- Table
FOR EACH ROW
DECLARE
V_Berufstart meine_Firma.Hiredate%TYPE; --Job begin
V_Geburtsdatum DATE; -- Date of birth
V_Alter number:=0; -- AGE
SELECT HIREDATE INTO V_Berufstart FROM meine_Firma;
BEGIN
V_Geburtsdatum:=('&Birthday');
V_Alter:= Round(MONTHS_BETWEEN(V_Berufstart,V_Geburtsdatum)-2)/12;
IF 16 > V_Alter THEN
RAISE_APPLICATION_ERROR(-20201,'Error when entering the date of birth');
END IF;
END;
/
SET SERVEROUTPUT OFF
If he under 16 then he may not work
sorry my english is not good (=
You have a much bigger issue in this script than the error you are getting. Even after correcting as #ShaunPeterson suggested it will still fail
, it WILL NOT generate an error it will just not run as you expect. The issue is you failed to understand substitution variables - the use of &name (Specifically here &Birthday.) I'll actually use &Birthday in the following but the discussion applies to ANY/ALL substitution variables.
people fail to understand why they can't use the "&" substitution
variables in their PL/SQL procedures and functions to prompt for input
at run time. This article will hopefully help clarify in your mind
what the differences are so that you can understand where and when to
use these.
Substitution Variables The clue here is in the name... "substitution". It relates to values being substituted into the code
before it is submitted to the database. These substitutions are
carried out by the interface being used
The effect of this substitution is the the line containing the substitution variable is physically rewritten by the interface replacing %Birthday. In this case if you don't enter a value or the date 2000-05-19 the statement before and after substitution is
BEFORE: V_Geburtsdatum:=('&Birthday');
AFTER: V_Geburtsdatum:=(''); OR V_Geburtsdatum:=('2000-05-19');
Either way the after is what the compiler sees; it does NOT see %Birthday at all. Moreover when run the trigger will not prompt for a value. As far as the compiler is concerned it is a hard coded value that will never change. Beyond that a trigger, or any other PLSQL script (stored or anonymous) never prompts for values, they are actually incapable of doing so as it is not part of the language. Any prompt is via your interface software not plsql.
I'm going to suggest a way to avoid the trigger altogether. Getting on soap box: Triggers are BAD, they have some usefulness for assigning auto incrementing keys (before 12c),logging, very limited auditing, etc. However, for business rules they should be the option of last resort. Ok Get off soap box.
The first thing is to make the columns meine_Firma.Hiredate and meine_Firma.Geburtsdatum NOT null (if not already). If either are NULL you cannot calculate anything with them, the result would be NULL.
Second create a new column age_at_hire (or whatever) as a virtual column then put a check constraint on it. And voila trigger no longer needed. See fiddle for demo.
So the proposed change (YES you will probably have to clean up the bad data first):
alter table meine_Firma modify
( hiredate not null
, Geburtsdatum not null
) ;
alter table meine_Firma add
( age_at_hire integer generated always as (trunc(months_between(hiredate,Geburtsdatum))) virtual
, constraint check_age_at_hire check (age_at_hire >= 16*12)
);
Anyway, I hope you get an understanding of substitution variables for the future. And learn to avoid triggers. Good luck.
The reason you are getting that specific error is that the below select will select ALL rows from meine_Firma as there is no where clause
SELECT HIREDATE INTO V_Berufstart FROM meine_Firma;
However because you are in a trigger you do not need to select anything you use the :NEW bind variable. So you can just use
V_Berufstart := :NEW.HIREDATE;
If this was an update trigger there would be both a :NEW and :OLD bind variable declared so that you can access the OLD and NEW values. As this is an Insert trigger the :OLD will just be null as there is no old values.
My understanding is there are 2 ways of returning retult from a procedure or function,
OUT type parameter: this is used to get values from procedures and functions
returned value from a function,
My question is,
what is the different between OUT parameter and returned value in a function?
If I have a OUT parameter in a function, does it mean I can have 2 "returned" result?
Returned values from functions that has no out parameters can be used from SQL queries (inside a select or dml statement). If you have a code that returns a simple primitive type, i reccomend function.
SELECT * FROM PAYMENTS WHERE DATE = LAST_DAY_OF_MONTH(SYSDATE)
If you needs to return two or more related values, you can create a pl/sql object and return it in a function, and it can be used from plain sql too!
SELECT RESP.CODE, RESP.BODY FROM (SELECT GET_HTTP_RESP(URL) FROM DUAL) RESP;
Functions can be out parameters too, but that functions, like procedures, cannot be called from SQL queries.
If the code is really not supposed to be called from a SQL query, and you like to return not related values or values that not are the main objective of that code, a procedure could be a good choice.
Not always you have a "correct" choice, is more like a programming style question.
Yes as suggested its a very huge topic but I can suggest some differences. Hope it Helps.
Answer :
1) Yes function can have multiple OUT IN params but the return type will be a constant. So if you have a OUT Param in a function so it cant be called in a SQL. Similarly FUNCTION without OUT param can be called in SQL.
2) You can use the OUT as well as RETURN type to get the values from FUNCTION but still return type remains only ONE.
CREATE OR REPLACE FUNCTION FUNCT_TEST
(
p_in IN NUMBER,
p_out OUT NUMBER
)
RETURN VARCHAR2
AS
BEGIN
p_out:=10;
RETURN 'AVRAJIT';
END;
SELECT FUNCT_TEST(1) FROM DUAL;
ORA-06553: PLS-306: wrong number or types of arguments in call to 'FUNCT_TEST'
CREATE OR REPLACE FUNCTION FUNCT_TEST
(
p_in IN NUMBER
)
RETURN VARCHAR2
AS
BEGIN
--p_out:=10;
RETURN 'AVRAJIT';
END;
SELECT FUNCT_TEST(1) FROM DUAL;
FUNCT_TEST(1)
AVRAJIT
You can add and use as many IN OUT parameters in Functions like Procedure (Limit of IN OUT parameters in Functions/Procedures is 65536).
But if you have created the Function with OUT parameters you will get below error while accessing the function from SELECT query:
ORA-06572: Function function_name has out arguments
I would like to ask you how would you increase the performance on Insert cursor in this code?
I need to use dynamic plsql to fetch data but dont know how to improve the INSERT in best way. like Bulk Insert maybe?
Please let me know with code example if possible.
// This is how i use cur_handle:
cur_HANDLE integer;
cur_HANDLE := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cur_HANDLE, W_STMT, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS2(cur_HANDLE, W_NO_OF_COLS, W_DESC_TAB);
LOOP
-- Fetch a row
IF DBMS_SQL.FETCH_ROWS(cur_HANDLE) > 0 THEN
DBMS_SQL.column_value(cur_HANDLE, 9, cont_ID);
DBMS_SQL.COLUMN_VALUE(cur_HANDLE, 3, proj_NR);
ELSE
EXIT;
END IF;
Insert into w_Contracts values(counter, cont_ID, proj_NR);
counter := counter + 1;
END LOOP;
You should do database actions in sets whenever possible, rather than row-by-row inserts. You don't tell us what CUR_HANDLE is, so I can't really rewrite this, but you should probably do something like:
INSERT INTO w_contracts
SELECT ROWNUM, cont_id, proj_nr
FROM ( ... some table or joined tables or whatever... )
Though if your first value there is a primary key, it would probably be better to assign it from a sequence.
Solution 1) You can populate inside the loop a PL/SQL array and then just after the loop insert the whole array in one step using:
FORALL i in contracts_tab.first .. contracts_tab.last
INSERT INTO w_contracts VALUES contracts_tab(i);
Solution 2) if the v_stmt contains a valid SQL statement you can directly insert data into the table using
EXECUTE IMMEDIATE 'INSERT INTO w_contracts (counter, cont_id, proj_nr)
SELECT rownum, 9, 3 FROM ('||v_stmt||')';
"select statement is assembled from a website, ex if user choose to
include more detailed search then the select statement is changed and
the result looks different in the end. The whole application is a web
site build on dinamic plsql code."
This is a dangerous proposition, because it opens your database to SQL injection. This is the scenario in which Bad People subvert your parameters to expand the data they can retrieve or to escalate privileges. At the very least you need to be using DBMS_ASSERT to validate user input. Find out more.
Of course, if you are allowing users to pass whole SQL strings (you haven't provided any information regarding the construction of W_STMT) then all bets are off. DBMS_ASSERT won't help you there.
Anyway, as you have failed to give the additional information we actually need, please let me spell it out for you:
will the SELECT statement always have the same column names from the same table name, or can the user change those two?
will you always be interested in the third and ninth columns?
how is the W_STMT string assembled? How much control do you have over its projection?
I created a user defined function to delete some data. It doesn't work with delete but works with select. I am Oracle 9i.
The function is something like this:
create or replace function UFN_PURGEDATA(INPUTID IN VarChar2) return number is
Result number;
begin
Result := 0;
DELETE FROM MyTable WHERE MyTable.ID=INPID;
COMMIT;
Result := 1;
EXCEPTION WHEN OTHERS THEN
return(Result);
end UFN_PURGEDATA;
Then I use select UFN_PURGEDATA('test') from dual to run it but got result 0.
The answer to your question is "no".
If you remove your error "handling" you will find that the delete is failing with an exception like:
ORA-14551: cannot perform a DML
operation inside a query
i.e. you cannot perform an insert, update or delete from within a function called in a SELECT statement.
To execute this function in an IDE or SQL Plus, wrap it in some more PL/SQL like this:
declare
l_result number;
begin
l_result := my_function(123);
end;
However, you will need to add a RETURN statement to your function first otherwise it will fail.
(NB I said "handling" above in quotes because it is really "mishandling" - it completely disguises the actual problem in a very unhelpful way.)
You can perform DML inside a function used in a SELECT if you add PRAGMA AUTONOMOUS_TRANSACTION. For example:
create or replace function UFN_PURGEDATA(INPUTID IN VarChar2) return number is
pragma autonomous_transaction;
begin
DELETE FROM MyTable WHERE MyTable.ID=INPUTID;
COMMIT;
return 1;
EXCEPTION WHEN OTHERS THEN
return 0;
end UFN_PURGEDATA;
/
But you definitely want to avoid this approach if possible. In general, there's no way to know how many times a function will be executed if it's used in a SELECT.
Yes you can delete rows using user-defined functions in Oracle, but not from within a SELECT statement.
There are a couple of problems with your code:
- you don't return a value if your function does not raise an exception
- you must not use a function performing DML in a SELECT statement; if you remove your exception block, you get an ORA-14551
Why not create a procedure (instead of function) with OUT parameter returning the number? Without doing the autonomous transaction trick, Oracle doesn't want you running functions (used in selects) with "side-effects" (understandable why we don't want a select to result in DML changes).