Oracle PL/SQL seems to ignore WHERE clause of my Function - oracle

I have a table of club´s members with a column of status that a want to use as a filter in my function below. But the result is always the same no matter what number I pass as a function parameter. The count always comes with the total registers of the entire table.
CREATE OR REPLACE FUNCTION fn_count_members
(id_status in club.members.id_status%TYPE)
RETURN NUMBER IS
total club.members.id%TYPE:=0;
BEGIN
SELECT COUNT(id) INTO total
FROM club.members m
WHERE m.id_status = id_status;
RETURN total;
END;
And the query:
select id_status, fn_count_members(1) as total from club.members;
What am I missing here?
Thank you.

You have two issues:
CREATE OR REPLACE FUNCTION fn_count_members(
p_id_status in club.members.id_status%TYPE -- change the parameter name to
-- something different from the
-- column name.
)
RETURN NUMBER IS
total club.members.id%TYPE:=0;
BEGIN
SELECT COUNT(id) INTO total
FROM club.members m
WHERE m.id_status = p_id_status; -- and here
RETURN total;
END;
/
Then you need to pass the parameter in rather than using 1:
select id_status,
fn_count_members(id_status) as total
from club.members;
db<>fiddle here

This is wrong:
(id_status in club.members.id_status%TYPE)
---------
this
Because, when used in where clause
WHERE m.id_status = id_status
it turns out to be something like where 1 = 1 and returns everything, no filtering at all.
Rename parameter to e.g.
(par_id_status in club.members.id_status%TYPE)
and use it as
WHERE m.id_status = par_id_status

Related

getting error of character string buffer too small even after changing to varchar(32000)

getting error QRA-06502 of character string buffer too small even after changing to varchar(32000)
create or replace function product_purchase(CUSTOMER_COD VARCHAR2) return varchar is
CODE VARCHAR2(32000);
BEGIN
CODE := ' ';
FOR PRODUCTS_INFO IN (SELECT CUSTOMER_CODE,PRODUCT_NAME
FROM ORDER_DETAIL JOIN ORDERS
ON ORDER_DETAIL.ORDER_ID = ORDERS.ORDER_ID)
LOOP
CODE := CODE || PRODUCTS_INFO.CUSTOMER_CODE||PRODUCTS_INFO.PRODUCT_NAME ||',';
END LOOP;
RETURN CODE;
END product_purchase;
/
ERROR MAINLY OCCURS WHILE SELECTING DATA AND PUTTING IT IN THE FUNCTION
SELECT CUSTOMER_CODE ,product_purchase(CUSTOMER_CODE),COUNT(PRODUCT_NAME)
FROM ORDER_DETAIL JOIN ORDERS
ON ORDER_DETAIL.ORDER_ID = ORDERS.ORDER_ID
GROUP BY CUSTOMER_CODE,product_purchase(CUSTOMER_CODE)
HAVING COUNT(PRODUCT_NAME)=3;
Even after changing to varchar2(32000)? Well, that doesn't guarantee that you'll get the result (obviously).
The way I see it, it is cursor query that is wrong. If you're calling a function as
product_purchase(CUSTOMER_CODE)
it means that you're passing a parameter to it (you didn't post the whole function code so I'll presume that parameter's name is par_customer_code; note that it shouldn't be just customer_code as it is equal to column name and you'll get unexpected result once you use the parameter).
Exactly - use the parameter. Code you wrote doesn't use it. Should've been
SELECT CUSTOMER_CODE,PRODUCT_NAME
FROM ORDER_DETAIL JOIN ORDERS
ON ORDER_DETAIL.ORDER_ID = ORDERS.ORDER_ID
WHERE customer_code = par_customer_code --> this
I guess that - applying that change - you won't have problems.
However, if you do, then check whether code you wrote actually works, run the cursor query itself and see how many rows it returns. Then, restrict number of rows. How? A simple way is to use rownum:
SELECT CUSTOMER_CODE,PRODUCT_NAME
FROM ORDER_DETAIL JOIN ORDERS
ON ORDER_DETAIL.ORDER_ID = ORDERS.ORDER_ID
WHERE customer_code = par_customer_code
AND rownum <= 10; --> this
and run your code again.
If it works, and if it turns out that varchar2 isn't capable of storing that much data, use CLOB datatype instead:
DECLARE
CODE CLOB; --> this

basic variables returning all results

I have the below variable.
DECLARE
v_clt NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE(v_clt);
END;
/
SELECT *
FROM CCP
WHERE CCP.ID = &v_clt
--AND CASE WHEN &v_clt < 1 THEN ID ELSE &v_clt END ID
I have may sub-queries within my query and I would like to be able to test the query by adding in one value for each sub queries, hence the variable and it works.
I also would like to be able to return all results at any given point too. Can this be accomplished?
You can use special value as 0 combine with decode (or case) to view all results
WHERE CCP.ID = decode(&v_clt,0,CCP.ID,&v_clt )

Oracle: How to create a function returning values for a "SELECT * FROM tab WHERE name IN (function())"

I have a problem which I can't solve. Maybe you have an idea about how to solve it.
I do have a given parameter-table like this:
P_VALUE P_NAME
----------- ----------
X85 A_03
XH1 A_04
XH2 A_04
XH3 A_04
C84 A_05
As you can see there are parameters with multiple entries. At the moment this parameters are used in this way:
SELECT * FROM tablex
WHERE code IN (SELECT p_value
FROM parameter_table
WHERE p_name LIKE 'A_04');
As the query is very big these parameter sub-select are used very often. I was trying to implement a function in Oracle to get my parameters. This works very fine as long as there is just 1 row per parameter. When I want to use it in "IN-Statements", it won't work because functions just return a single value.
--WORKS
SELECT * FROM tablex
WHERE code = (f_get_param('A_03'));
--DOES NOT WORK
SELECT * FROM tablex
WHERE code IN (f_get_param('A_04'));
Please note that I need it for plain SQL statements, so procedures won't work as they are just good for PL/SQL.
I would be really thankful for good ideas or help!
Use collections. Here you have an example http://www.adp-gmbh.ch/ora/plsql/coll/return_table.html
Technically you can achieve using the function this way but doing this will cause index not to be used on code column on tablex and may affect performance .Using function index you can reduce performance impact
CREATE OR REPLACE FUNCTION f_get_param(p_value1 IN VARCHAR2,p_name1 in VARCHAR2) return NUMBER
DETERMINISTIC
IS
l_count NUMBER;
BEGIN
select count(1) into l_count from parameter_table where p_value =p_value1
and p_name=p_name1;
if l_count > 0
then
return 1;
else
return 0;
end if;
end f_get_param;
AND use the select statement like this
SELECT * FROM tablex
WHERE f_get_param(code,'A_04')=1;
EDIT 1:-
Also to reduce the performance impact in database 10.2 and greater If the parameter_table is static you can use the DETERMINISTIC clause in the Function to say that the function returns the same value if called with same parameters every time
Please find the link on the article about using functions in SELECT statement
--DOES NOT WORK
SELECT * FROM tablex
WHERE code IN (f_get_param('A_04'));
-- Try this
SELECT * FROM tablex
WHERE code IN (select * from TABLE(f_get_param('A_04')));
You have to "CAST" a collection onto SQL TABLE.
Also when you use cast you can also use inner joint:
SELECT * FROM tablex join TABLE(f_get_param('A_04') using (code);
I think - generally - your problem is called "Dynamic where clause". Try to search some articles about it on AskTom.
I think the actual solution to your problem is to simply join the two tables and create the appropriate indexes rather than invoking a PL/SQL function at all:
SELECT x.* FROM tablex x, parameter_table p
WHERE x.code = p.p_value
AND p.p_name LIKE '%A_04%';
Note that you also have a semantic error in your LIKE clause. You're not using the % sign therefore your LIKE 'A_04' is just the same as = 'A_04'

Sorting by value returned by a function in oracle

I have a function that returns a value and displays a similarity between tracks, i want the returned result to be ordered by this returned value, but i cannot figure out a way on how to do it, here is what i have already tried:
CREATE OR REPLACE PROCEDURE proc_list_similar_tracks(frstTrack IN tracks.track_id%TYPE)
AS
sim number;
res tracks%rowtype;
chosenTrack tracks%rowtype;
BEGIN
select * into chosenTrack from tracks where track_id = frstTrack;
dbms_output.put_line('similarity between');
FOR res IN (select * from tracks WHERE ROWNUM <= 10)LOOP
SELECT * INTO sim FROM ( SELECT func_similarity(frstTrack, res.track_id)from dual order by sim) order by sim; //that's where i am getting the value and where i am trying to order
dbms_output.put_line( chosenTrack.track_name || '(' ||frstTrack|| ') and ' || res.track_name || '(' ||res.track_id|| ') ---->' || sim);
END LOOP;
END proc_list_similar_tracks;
/
declare
begin
proc_list_similar_tracks(437830);
end;
/
no errors are given, the list is just presented unsorted, is it not possible to order by a value that was returned by a function? if so, how do i accomplish something like this? or am i just doing something horribly wrong?
Any help will be appreciated
In the interests of (over-)optimisation I would avoid ordering by a function if I could possibly avoid it; especially one that queries other tables. If you're querying a table you should be able to add that part to your current query, which enables you to use it normally.
However, let's look at your function:
There's no point using DBMS_OUTPUT for anything but debugging unless you're going to be there looking at exactly what is output every time the function is run; you could remove these lines.
The following is used only for a DBMS_OUTPUT and is therefore an unnecessary SELECT and can be removed:
select * into chosenTrack from tracks where track_id = frstTrack;
You're selecting a random 10 rows from the table TRACKS; why?
FOR res IN (select * from tracks WHERE ROWNUM <= 10)LOOP
Your ORDER BY, order by sim, is ordering by a non-existent column as the column SIM hasn't been declared within the scope of the SELECT
Your ORDER BY is asking for the least similar as the default sort order is ascending (this may be correct but it seems wrong?)
Your function is not a function, it's a procedure (one without an OUT parameter).
Your SELECT INTO is attempting to place multiple rows into a single-row variable.
Assuming your "function" is altered to provide the maximum similarity between the parameter and a random 10 TRACK_IDs it might look as follows:
create or replace function list_similar_tracks (
frstTrack in tracks.track_id%type
) return number is
sim number;
begin
select max(func_similarity(frstTrack, track_id)) into sim
from tracks
where rownum <= 10
;
return sim;
end list_similar_tracks;
/
However, the name of the function seems to preclude that this is what you're actually attempting to do.
From your comments, your question is actually:
I have the following code; how do I print the top 10 function results? The current results are returned unsorted.
declare
sim number;
begin
for res in ( select * from tracks ) loop
select * into sim
from ( select func_similarity(var1, var2)
from dual
order by sim
)
order by sim;
end loop;
end;
/
The problem with the above is firstly that you're ordering by the variable sim, which is NULL in the first instance but changes thereafter. However, the select from DUAL is only a single row, which means you're randomly ordering by a single row. This brings us back to my point at the top - use SQL where possible.
In this case you can simply SELECT from the table TRACKS and order by the function result. To do this you need to give the column created by your function result an alias (or order by the positional argument as already described in Emmanuel's answer).
For instance:
select func_similarity(var1, var2) as function_result
from dual
Putting this together the code becomes:
begin
for res in ( select *
from ( select func_similarity(variable, track_id) as f
from tracks
order by f desc
)
where rownum <= 10 ) loop
-- do something
end loop;
end;
/
You have a query using a function, let's say something like:
select t.field1, t.field2, ..., function1(t.field1), ...
from table1 t
where ...
Oracle supports order by clause with column indexes, i.e. if the field returned by the function is the nth one in the select (here, field1 is in position 1, field2 in position 2), you just have to add:
order by n
For instance:
select t.field1, function1(t.field1) c2
from table1 t
where ...
order by 2 /* 2 being the index of the column computed by the function */

Force Oracle to process one row at a time

I have a query that in the select statement uses a custom built function to return one of the values.
The problem I have is every now and then this function will error out because it returns more than one row of information. SQL Error: ORA-01422: exact fetch returns more than requested number of rows
To further compound the issue I have checked the table data within the range that this query should be running and can't find any rows that would duplicate based on the where clause of this Function.
So I would like a quick way to identify on which Row of the original query this crashes so that I can take the values from that query that would be passed into the function and rebuild the Functions query with these values to get it's result and see which two or more rows are returned.
Any ideas? I was hoping there could be a way to force Oracle to process one row at a time until it errors so you can see the results UP to the first error.
Added the code:
FUNCTION EFFPEG
--Returns Effective Pegged Freight given a Effdate, ShipTo, Item
DATE1 IN NUMBER -- Effective Date (JULIANDATE)
, SHAN IN NUMBER -- ShipTo Number (Numeric)
, ITM IN NUMBER -- Short Item Number (Numeric)
, AST IN VARCHAR -- Advance Pricing type (varchar)
, MCU IN VARCHAR Default Null --ShipFrom Plant (varchar)
) RETURN Number
IS
vReturn Number;
BEGIN
Select ADFVTR/10000
into vReturn
from PRODDTA.F4072
where ADEFTJ <= DATE1
and ADEXDJ >= DATE1
and ADAN8 = SHAN and ADITM = ITM
and TRIM(ADAST) = TRIM(AST)
and ADEXDJ = (
Select min(ADEXDJ) ADEXDJ
from PRODDTA.F4072
where ADEFTJ <= DATE1
and ADEXDJ >= DATE1
and ADAN8 = SHAN
and ADITM = ITM
and TRIM(ADAST) = TRIM(AST));
Query that calls this code and passes in the values is:
select GLEXR, ORDTYPE,
EFFPEG(SDADDJ, SDSHAN, SDITM, 'PEGFRTT', SDMCU),
from proddta.F42119
I think the best way to do it is trough Exceptions.
What you need to do is to add the code to handle many rows exception in your function:
EXCEPTION
WHEN TOO_MANY_ROWS THEN
INSERT INTO ERR_TABLE
SELECT your_columns
FROM query_that_sometimes_returns_multiple_rows
In this example the doubled result will go to separated table or you can decide to simply print out with dbms_output.
An easy page to start can be this, then just google exception and you should be able to find all you need.
Hope this can help.

Resources