How to get a row object from a function that return a table object in plsql? - plsql

i'm trying to get a single row and save it into a variable in PLSQL.
I'm using APEX and all is around the APEX_DATA_PARSE.PARSE() function.
As the docs said, .PARSE() should return WWV_FLOW_T_PARSER_ROW for every row of results.
What i'm trying to do is to get only one row to be saved into a variable.
This is My code:
`
DECLARE
r_columns_headers WWV_FLOW_T_PARSER_ROW; --object
BEGIN
DBMS_OUTPUT.ENABLE;
BEGIN
<<populate_headers>>
select * into r_columns_headers
from apex_application_temp_files f,
table( apex_data_parser.parse(
p_content => f.blob_content,
p_add_headers_row => 'Y',
P_SKIP_ROWS => 1,
p_max_rows => 500,
p_store_profile_to_collection => 'FILE_PARSER_COLLECTION',
p_file_name => f.filename ) ) p
where f.name = '43300947378776117/DATALOAD_Test_data_main_v032.xlsx' and p.line_number = 2;
end populate_headers;
DBMS_OUTPUT.PUT_LINE(r_columns_headers.id);
end;
`
is just for test outside the main package that i'm writing.
The error i get is PL/SQL: ORA-00947 on row 8, on the select * into r_columns_headers section.
I don't know why i get not enough values, the fields are the same as they are the same type of object. The select return exactly one row, of a WWV_FLOW_T_PARSER_TABLE object. Not all columns are with data, someones are null, is this the problem?
I'm just at the beginning of learning plsql.
Thanks a lot
wwv_flow_t_parser_row
I'm trying to get a row into the r_columns_headers variable to access it in my program.

The "ORA-00947: not enough values" error is sometimes a bit confusing; in a PL/SQL context it can be backwards.
The problem is that you actually have too many values in your select list. You are doing:
select * into r_columns_headers
but you're joining two table expression, apex_application_temp_files as f and the unnested table as p. So the * is effectively doing:
select f.*, p.* into r_columns_headers
... and that object doesn't have fields for all the columns coming from f.
So you need to restrict it to the relevant data:
select p.* into r_columns_headers
... but that returns all of the object attributes as individual columns, not as a single object; but you can get the actual object with the value() function:
select value(p) into r_columns_headers
fiddle with a dummy object type and data as the real APEX types aren't available there.

Related

expression is of wrong type for function

CREATE OR REPLACE FUNCTION k_w_b_salary(k IN NUMBER, b IN BOOLEAN)
RETURN EMP.ENAME%TYPE IS
names name_table;
BEGIN
IF (b = true) THEN
SELECT ENAME BULK COLLECT INTO names
FROM
(SELECT *
FROM EMP
ORDER BY SAL ASC)
WHERE ROWNUM <= k;
RETURN names;
ELSIF (b = false) THEN
SELECT ENAME BULK COLLECT INTO names
FROM
(SELECT *
FROM EMP
ORDER BY SAL DESC)
WHERE ROWNUM <= k;
RETURN names;
END IF;
END;
And I get this error:
12/9 PL/SQL: Statement ignored
12/16 PLS-00382: expression is of wrong type
20/9 PL/SQL: Statement ignored
20/16 PLS-00382: expression is of wrong type
I have this function that tries to find the best/worst paid employees. But i get the above error.
I think it's something to do with the ROWNUM but I am not sure.
I think the lines the error points out are not the lines with the error.
I had this function writen differently and the lines in the error where pointing to the ROWNUM <= k lines.
I have tried putting a fixed number there (<= 3) for example and I got the same error.
I have no idea what else to try, i can't really understand why this is not working.
It's not obvious to me why this is not working. I think it should work fine but obviously it dousen't.
The code for the table i use is :
CREATE OR REPLACE TYPE name_table IS TABLE OF VARCHAR2(10);
Any help is appreciated!
In the function declaration, you said
RETURN EMP.ENAME%TYPE
I assume the data type of column ENAME in table EMP is some sort of string (VARCHAR2(40) or similar) - right?
In the declarations section, you declare a variable names of data type name_table. You didn't show us the definition of the name_table type (that must be given outside the function, not in it); we can probably assume it is a nested table of some sort. Right? [EDIT - I take that back; you did show us your definition of name_table, at the end of your question.]
In the end, your function returns names. Which is of type name_table. But you said the function returns something else: EMP.ENAME%TYPE. In particular, you said the function returns a scalar data type, but you are returning a collection.
This will not work even if the collection has a single element. A table with a single "record" is not the same data type as the "record" itself - even if an actual table has a single "record" in it.
(And, much more so, when the table has three records in it!)
Rather: It seems that you want a table function: one that returns a table of things. If so, then declare the function that way. Perhaps you want the function to
RETURN NAME_TABLE
(at the top, in the function declaration)

how to pass current row into a oracle user defined function

I want to create an inline oracle function which will refer the column values of the current row being fetched and to return a value as per my conditions. I tried the same by passing ROWID into function and in the function body current row will be fetched using ROWID and manipulate the values.
CREATE FUNCTION CHECK_STATUS(P_ROWID) RETURN VARCHAR2 IS
V_ROW MY_TBL%TYPE
BEGIN
SELECT * INTO V_ROW FROM MY_TBL WHERE ROWID=P_ROWID;
IF V_ROW.COL1 IS NOT NULL AND
V_ROW.COL2 IS NOT NULL AND
V_ROW.COL3 IS NOT NULL THEN
RETURN 'OK';
ELSE
RETURN 'INCOMPLETE';
END IF;
END;
This function is further called at various places as
SELECT A.*,CHECK_STATUS(ROWID) FROM MY_TBL A;
it works but slows down the query badly over a thousand records because on each fetch of rows function will again do a select query for getting the column values. I know that this can also be done as
CREATE FUNCTION CHECK_STATUS(COL1,COL2,COL3) RETURN VARCHAR2 IS
here the problem is that i want to nearly check 9 column values in the function and it looks odd to write the column name during every call of the function. I doubt is there any method that the function can refer the current row being fetched without receiving ROWID/ COLUMNS through parameter something like (THIS.COL1,THIS.COL2,THIS.COL3)
You could add a virtual column for this, using a case expression, and not have a function at all:
alter table my_tbl add (status generated always as
(case when column1 is null or column2 is null or column3 is null
-- etc., all 9 columns or whatever else you want to check
then 'INCOMPLETE' else 'OK' end) virtual);
You can then query it with:
select column1, column2, ..., status from my_tbl
You can also add an index on the virtual column if that's useful for how you'll use it.
You could still use a (deterministic) function if you wanted to, with all the column values passed in - the 'looks odd' part would be hidden in the DDL, so queries wouldn't see it. It doesn't sound like using a function would add much in this case though.

PLS-00487 Error-Invalid reference to Variable 'CHAR'

I'm designing a function that is part of a larger package. The function is intended to take a District Code and return a collection of unique IDs for 10-15 stores that are assigned to that district. The function is intended to return a collection that can be queried like a table, i.e., using the TABLE function in a SQL statement.
I've created the following Types:
Schema Level type:
create or replace TYPE HDT_CORE_ORGIDS AS TABLE OF CHAR(20);
and a Type inside the Package
TYPE CORE_ORGIDS IS TABLE OF CHAR(20) INDEX BY BINARY_INTEGER;
Here's the function code:
FUNCTION FindDistrictOrgs(
ParamOrgCode VARCHAR2
)
RETURN HDT_CORE_ORGIDS
AS
ReturnOrgs HDT_CORE_ORGIDS := HDT_CORE_ORGIDS();
FDOTemp HDT_CORE_MAIN.CORE_ORGIDS;
i BINARY_INTEGER := 0;
CURSOR FDOCurr IS
SELECT org.id AS OrgID
FROM tp2.tpt_company org
WHERE LEVEL = 2
START WITH org.name = ParamOrgCode
CONNECT BY PRIOR org.id = org.parent_id;
BEGIN
OPEN FDOCurr;
LOOP
i := i +1;
FETCH FDOCurr INTO FDOTemp(i);
EXIT WHEN FDOCurr%NOTFOUND;
END LOOP;
IF FDOTemp.EXISTS(FDOTemp.FIRST) THEN
ReturnOrgs.EXTEND(FDOTemp.LAST);
FOR x IN FDOTemp.FIRST .. FDOTemp.LAST LOOP
ReturnOrgs(x) := FDOTemp(x).OrgID;
END LOOP;
END IF;
CLOSE FDOCurr;
RETURN ReturnOrgs;
END FindDistrictOrgs ;
I'm getting the PLS-00487:Invalid Reference to variable 'CHAR' at the line:
ReturnOrgs(x) := FDOTemp(x).OrgID;
I've double-checked at the value returned by the SQL (the org.id AS OrgID) is of the CHAR(20 BYTE) datatype.
So...what's causing the error?
Any help is appreciated! :)
OrgID is the alias you gave the column in your cursor, it has no meaning to the collection. Since both collections are of simple types you should just be doing:
ReturnOrgs(x) := FDOTemp(x);
The syntax you're using is implying FDOTemp is a collection of objects and you're trying to reference the OrgID attribute of an object; but since CHAR isn't an object type, this errors. The error message even makes some sense when viewed like that, though it's not terribly helpful if you don't already know what's wrong... and not entirely helpful when you do.
Incidentally, you could use a bulk collect to populate the collection without the cursor or loops, or the extra collection:
SELECT org.id
BULK COLLECT INTO ReturnOrgs
FROM tp2.tpt_company org
WHERE LEVEL = 2
START WITH org.name = ParamOrgCode
CONNECT BY PRIOR org.id = org.parent_id;
RETURN ReturnOrgs;

Debugging a function with table type as input parameter

Can anybody let me know how can i debug a functions with table type as input parameter and this function returns a table type pipelined.
Please see below details.When i try to test the function it creates below anonymous block but when i click on debug button it gives error:
Anonymous block:
declare
-- Non-scalar parameters require additional processing
result t_bmk_q;
pit_srch_str t_parm;
begin
-- Call the function
result := f_bmk_srch(pit_srch_str => pit_srch_str,
piv_op => 'ALL');
end;
---f_bmk_q function returns table type t_bmk_q pipelined
defintions:
==============
t_bmk_q --->table type
t_bmk_q is TABLE OF r_bmk_q -->object of some attributes.
pit_srch_str ---> is parameter of type t_parm which is table type of r_parm
--plz see def of r_parm:
CREATE OR REPLACE TYPE r_parm AS OBJECT
(
p_abc varchar2(200)
,p_new_val varchar2(2000)
,CONSTRUCTOR FUNCTION r_parm
(
p_abc varchar2
,p_new_val varchar2
) RETURN SELF AS RESULT
);
Example:I have below sample values to test and debug:
r_parm('TAB1.VALUE','123321123')
Thanks
Rajesh
It seems you are using a PL/SQL Developer Test Window to run the tests. I recognise the comment Non-scalar parameters require additional processing.
PL/SQL Developer's Test Window doesn't handle pipelined functions too well.
You are best off removing the result variable and wrapping the function call in open :cursor for select * from table(...). Add a variable named cursor of type Cursor to the variables list below the window.
To populate the input table, you can simply 'call' the table type, passing each row in the table as a separate argument. For example,
t_parm(r_parm(...), r_parm(...), r_parm(...))
You can add as many rows to the table as you like this way.
Putting both of these changes together, we have something like the following:
declare
pit_srch_str t_parm;
begin
-- Create input table.
pit_srch_str := t_parm(
r_parm('TAB1.VALUE','123321123'),
r_parm('TAB2.VALUE','456597646')
);
-- Call the function
open :cursor for select * from table(f_bmk_srch(pit_srch_str => pit_srch_str,
piv_op => 'ALL'));
end;
/
Once you've run the function, you can get the results from the cursor variable (use the ... button at the far right).

PL/SQL - LAST_VALUE return more than one row?

I am doing an school assigment where I need to get the last value of "code" so I can then insert next row with this code incremented. I tried to pull it out this way.
DECLARE
v_last_code f_shifts.code%TYPE;
BEGIN
SELECT LAST_VALUE(code) OVER (ORDER BY code)
INTO v_last_code
FROM f_shifts;
DBMS_OUTPUT.PUT_LINE('Last value is: ' || v_last_code);
END;
However I get ORA-01422: exact fetch returns more than one requested number of rows
and I have no idea why and how can a last_value be more than one row
Thanks !
You can use a nested table like this.
DECLARE
v_last_code f_shifts.code%TYPE;
TYPE t_tbl IS TABLE OF f_shifts.code%TYPE;
-- Above line creates the nested table type of the required type.
v_tbl T_TBL;
-- Above line creates the nested table variable.
BEGIN
SELECT code
BULK COLLECT INTO v_tbl -- collects all the values, ordered, into the nested table
FROM f_shifts
ORDER BY code;
v_last_code = v_tbl(v_tbl.LAST); -- gets the last values and puts into the variable
DBMS_OUTPUT.PUT_LINE('Last value is: ' || v_last_code);
END;
/

Resources