wrong updation of data using collection but insertion is possible - oracle

create or replace function get_ware_house_branch(p_WAREHOUSE_IDS in varchar2,
p_PLACE_ID in varchar2)
return id_warehouse_list
is
l_warehouse_list id_warehouse_list := id_warehouse_list();
str varchar2(300);
begin
str := 'SELECT BRANCH_WAREHOUSE(w.wh_id, w.wh_name)
FROM POD_WAREHOUSE_MASTER W
where ( W.wh_id IN (' ||p_WAREHOUSE_IDS || '))';
execute immediate str bulk collect into l_warehouse_list;
for i in l_warehouse_list.first..l_warehouse_list.last loop
dbms_output.put_line(l_warehouse_list(i).wh_id || ', ' || l_warehouse_list(i).wh_name);
/*update pod_place_warehouse_mapping_tb
set wh_id = l_warehouse_list(i).wh_id
where place_id = p_PLACE_ID ;*/
insert into pod_place_warehouse_mapping_tb (id,place_id ,wh_id )
values
(POD_UNIQUE_VAL_SEQ.NEXTVAL ,p_PLACE_ID,l_warehouse_list(i).wh_id);
end loop;
commit;
return l_warehouse_list;
end;

The result you're seeing is exactly what the commented-out UPDATE statement says you want to do. In the update case you could replace the loop with a single update statement:
UPDATE POD_PLACE_WAREHOUSE_MAPPING_TB
SET WH_ID = l_warehouse_list(l_warehouse_list.LAST).WH_ID
WHERE PLACE_ID = p_PLACE_ID
The loop and the UPDATE statement above will produce the exact same results. In the loop case, you're updating all rows in POD_PLACE_WAREHOUSE_MAPPING_TB with each value from l_warehouse_list, one after the other. When the loop is complete all rows in POD_PLACE_WAREHOUSE_MAPPING_TB which have PLACE_ID = p_PLACE_ID will have their WH_ID column set to the WH_ID value from the l_warehouse_list element with the highest index.
I have no idea what other result you're expecting. I think you need to re-think what it is you're trying to do.
Best of luck.

Related

Oracle create function using cursors

I have a requirement to create a function in which I have to pass Query result as input to the output query concatenate by space . The below code is roughly written. Need help in modifying the function.
CREATE or replace FUNCTION GETPGM(Year IN Number, ID IN Number)
RETURN VARCHAR2 IS
result VARCHAR2(200);
cursor getterm is
select term_code from table_term
where proc_yr = Year;
BEGIN
loop
fetch cur into TERM;
exit when cur%NOTFOUND;
select f_getp (ID,:TERM1,Year)||' ' f_getp (ID,:TERM2,Year) from dual -- output Result set
end loop;
RETURN result;
END;
Let me know if any doubts.
If you want to apply the f_getp function to every row of the query result and concatenate the results into a space delimited string then you do not need to use a cursor and can use LISTAGG:
CREATE FUNCTION GETPGM(
i_year IN table_term.proc_yr%type,
i_id IN Number
) RETURN VARCHAR2
IS
result VARCHAR2(200);
BEGIN
SELECT LISTAGG(f_getp(i_id, term_code, i_year), ' ') WITHIN GROUP (ORDER BY term_code)
INTO result
FROM table_term
WHERE proc_yr = i_year;
RETURN result;
END;
/
It is unclear what TERM1 and TERM2 are (parameters? If so, you should pass them to the function), nor what is the result supposed to be.
Anyway, see if something like this helps:
CREATE OR REPLACE FUNCTION getpgm (par_year IN NUMBER,
par_id IN NUMBER,
par_term1 IN NUMBER,
par_term2 IN NUMBER)
RETURN VARCHAR2
IS
result VARCHAR2 (200);
BEGIN
FOR cur_r IN (SELECT term_code
FROM table_term
WHERE proc_yr = par_year)
LOOP
result :=
result
|| ' '
|| f_getp (par_id, par_term1, par_year)
|| ' '
|| f_getp (par_id, par_term2, par_year);
END LOOP;
RETURN result;
END;
result should be concatenated with its "previous" value (otherwise, you'd get the last cursor's value as the result, not everything)
use cursor FOR loop as Oracle does all the dirty job for you (you don't have to declare cursor variable, open the cursor, fetch from it, worry about exiting the loop, close the cursor - note that a lot of those things your code doesn't have, while it should)
pay attention to return value's datatype; will a string whose length is 200 characters enough? The result will be a space-separated list of some values. Wouldn't you rather return a ref cursor or a collection?

My Trigger getting looping and Case Error when i try to join two tables on it

I have 2 tables, Contract and Bankslip.
I need to get the date field from the Contract table, and set the date on Bankslip table, but it's getting in a loop, I think!
How can i do it?
Here is my code:
create or replace TRIGGER GFLANCAM_ATUALIZA_DATA_EMISSAO
BEFORE INSERT ON GFLANCAM
FOR EACH ROW
DECLARE
DATA_INICIO_CONTRATO DATE;
BEGIN
CASE WHEN :NEW.DOCUMENTO <> ' ' then
SELECT dt_inicio
INTO DATA_INICIO_CONTRATO
from ctcontra
where cd_contrato = :NEW.documento;
:NEW.data := DATA_INICIO_CONTRATO;
END CASE;
END;
What am I doing wrong?
Much of the trigger is unnecessary.
You can accomplish your goal without the CASE and without defining a variable.
CREATE OR REPLACE TRIGGER GFLANCAM_ATUALIZA_DATA_EMISSAO
BEFORE INSERT
ON GFLANCAM
FOR EACH ROW
BEGIN
-- Consider following:
-- IF NVL (:NEW.DOCUMENTO, ' ') <> ' '
IF :NEW.DOCUMENTO <> ' '
THEN
-- Following line may cause ORA-01403: no data found
SELECT dt_inicio INTO :NEW.data FROM ctcontra WHERE cd_contrato = :NEW.documento;
END IF;
END;
/
A few notes:
If you want to catch NULL values then add the NVL shown above.
Watch out for the case where a corresponding record is not found in ctcontra--this condition would result in ORA-01403: no data found (which might be exactly what you want in this case).
Make sure that ctcontra has only one record for each cd_contrato value, otherwise you will get a ORA-01422: exact fetch returns more than requested number of rows.
Take a look at the update:
{CREATE OR REPLACE TRIGGER GFLANCAM_ATUALIZA_DATA_EMISSAO
AFTER INSERT ON GFLANCAM
FOR EACH ROW
DECLARE
DATA_INICIO_CONTRATO DATE;
BEGIN
IF DOCUMENTO <> ' ' THEN
SELECT dt_inicio INTO DATA_INICIO_CONTRATO from ctcontra where cd_contrato =
DOCUMENTO;
UPDATE GFLANCAM SET DATA = DATA_INICIO_CONTRATO;
END IF;
END;}

function not returning a value for no rows returned

I created a function that takes a movie id as input and returns stock information based from the ID. The function mostly works but if I want to retrieve information from a movie that is not in the database(returns no rows) nothing returns. Can't figure out why?
doesn't give me an error when i call an ID that returns no rows so exception handling wouldn't work.
create or replace function stock_info
(p_id IN NUMBER
)
return VARCHAR2
IS
cursor c1 is
select movie_id, movie_title, movie_qty
from mm_movie
where p_id = movie_id;
lv_movie_info VARCHAR2(100);
BEGIN
for i in c1 loop
if p_id = i.movie_id then
lv_movie_info := i.movie_title || ' is available: ' || i.movie_qty || ' on the shelf';
else
lv_movie_info := 'no data found';
end if;
end loop;
return lv_movie_info;
END STOCK_INFO;
/
The reason you don't get anything when there is no data is that the loop doesn't execute. Logically the For expression says "execute the following loop for every row returned in the cursor" but there are no rows in the cursor so it never executes the loop. Further the structure actually indicates you are expecting multiple for a given p_id. If that's not the case you can eliminate the cursor all together. Assuming p_id is the primary key you have either 0 or 1 row so:
create or replace function stock_info (p_id in number)
return text
is
lv_movie_info varchar2(100);
begin
select i.movie_title || ' is available: ' || i.movie_qty || ' on the shelf'
into lv_movie_info
from mm_movie i
where p_id = movie_id;
return lv_movie_info;
exceptions
when no_data_found
then return 'no data found';
end stock_info;
Of course if do expect more that 1 row the cursor is needed, but the IF is not as the were clause guarantees it's true. Still with 0 rows the loop will not be executed so the 'no data found' message needs to go after "End Loop".
Belayer
the cursor statement you used fetches data from the in parameter. i.e., in the cursor select you limiting based on the movie id passed.
on passing a movie id which is not in the data base, the cursor select statement would not fetch any records, and so the flow won't even go inside the for loop.
if you wanted to return no data found - on passing a movie id which is not in the database, two ways to resolve
1. before the loop, have select statement to set a flag to Y or N if exists according and to have your requirement.
2. in if not using for cursor, there is an option to check not found...
sample:
declare
cursor c1 is select * from table_sample; -- empty table
c_rec c1%rowtype;
begin
open c1;
fetch c1 into c_rec;
if c1%notfound then
dbms_output.put_line('not found');
end if;
close c1;
end;
-- output
not found

Is this a table of tables in PL/SQL (If not what is going on with this code?)

Can someone clarify what the PL/SQL code below is doing? It looks as if assets_type is a table of base_Asset. Does that make it a Table of Tables?
I'm having a difficult time visualizing this when it comet to populating the data:
assets(v_ref_key)(dbfields(i).field) := rtrim(replace(strbuf_long2,'''',''''''));
Is this similar to a two dimensional array? Does this mean to populate the field column in the assets (temporary) table with an index of v_ref_key?
PROCEDURE LOAD
IS
TYPE dbfields_rec IS RECORD (field dbfields.field%TYPE,
article_title dbfields.title%TYPE,
image_title dbfields.title%TYPE);
TYPE dbfields_type IS TABLE OF dbfields_rec INDEX BY BINARY_INTEGER;
TYPE base_Asset IS TABLE OF VARCHAR2(4000) INDEX BY VARCHAR2(32);
TYPE assets_type IS TABLE OF asset_type INDEX BY VARCHAR2(4000);
dbfields dbfields_type;
assets assets_type;
v_ref_key assets.ref_key%TYPE;
-- CLIPPED Populate dbfields array code
-- It correctly populates
FOR i IN 1..dbfields.COUNT LOOP
BEGIN
sqlbuf := '(select rtrim(ufld3), ' || dbfields(i).field ||
' as col_label from assetstable ' ||
' where rtrim(ufld3) = ' || '''' || in_id || '''' || ' )';
OPEN assets_cur FOR
sqlbuf;
LOOP
FETCH assets_cur INTO v_ref_key, strbuf_long2;
EXIT WHEN assets_cur%NOTFOUND;
IF (trim(strbuf_long2) is not null and dbfields(i).field is not null) THEN
assets(v_ref_key) (dbfields(i).field)
:= rtrim(replace(strbuf_long2,'''',''''''));
END IF;
END LOOP;
close assets_cur;
END;
END LOOP;
END LOAD;
PL/SQL really only provides one-dimensional arrays - but each element of the array is allowed to be another array if you want to make arrays that act like multi-dimensional ones.
Here is an awfully contrived example to illustrate:
DECLARE
TYPE rows_type IS TABLE OF VARCHAR2(4000) INDEX BY VARCHAR2(4000);
TYPE spreadsheet_type IS TABLE OF row_type INDEX BY VARCHAR2(4000);
spreadsheet spreadsheet_type;
BEGIN
spreadsheet ('row 1') ('column A') := 'XYZ';
END;
The first ('row 1') is the index into the spreadsheet_type array, which would hold all the columns for a particular "row"; and the second ('column A') is the index into the rows_type array.
The "multi-dimensional" aspect of this implementation isn't perfect, however: while you can work with a whole row if you want, e.g.:
my_row rows_type;
...
my_row := spreadsheet ('row 1');
you can't pick out a particular column - there's no way to refer to the set of all elements in the rows for a particular index into rows_type. You'd have to do something like make another type and loop through the first array.

Oracle PL\SQL Null Input Parameter WHERE condition

As of now I am using IF ELSE to handle this condition
IF INPUT_PARAM IS NOT NULL
SELECT ... FROM SOMETABLE WHERE COLUMN = INPUT_PARAM
ELSE
SELECT ... FROM SOMETABLE
Is there any better way to do this in a single query without IF ELSE loops. As the query gets complex there will be more input parameters like this and the amount of IF ELSE required would be too much.
One method would be to use a variant of
WHERE column = nvl(var, column)
There are two pitfalls here however:
if the column is nullable, this clause will filter null values whereas in your question you would not filter the null values in the second case. You could modify this clause to take nulls into account but it turns ugly:
WHERE nvl(column, impossible_value) = nvl(var, impossible_value)
Of course if somehow the impossible_value is ever inserted you will run into some other kind of (fun) problems.
The optimizer doesn't understand correctly this type of clause. It will sometimes produce a plan with a UNION ALL but if there are more than a couple of nvl, you will get full scan even if perfectly valid indexes are present.
This is why when there are lots of parameters (several search fields in a big form for example), I like to use dynamic SQL:
DECLARE
l_query VARCHAR2(32767) := 'SELECT ... JOIN ... WHERE 1 = 1';
BEGIN
IF param1 IS NOT NULL THEN
l_query := l_query || ' AND column1 = :p1';
ELSE
l_query := l_query || ' AND :p1 IS NULL';
END IF;
/* repeat for each parameter */
...
/* open the cursor dynamically */
OPEN your_ref_cursor FOR l_query USING param1 /*,param2...*/;
END;
You can also use EXECUTE IMMEDIATE l_query INTO l_result USING param1;
This should work
SELECT ... FROM SOMETABLE WHERE COLUMN = NVL( INPUT_PARAM, COLUMN )

Resources