Setting the value of a form field from a Query - oracle

I have a form field where one of the values has a default value defined in a an application settings table. The user would see the default value upon display of the create form, but could change it to another value if they wanted prior to saving the new row.
I don't see any way in the field defaults to specify that the default value is the result of an SQL query (e.g. select default_rate from app_defaults where row_key = 1).
Surely this has to be possible, but how?

As posted to Jeffrey above, final solution can be done completely within APEX but using the Default Value Type = PL/SQL Function Body on the page item.
DECLARE default_value number(9,2);
BEGIN
SELECT deflt_rate INTO default_value FROM app_defaults WHERE row_key = 1;
RETURN default_value;
END;

You can use the SQL query in a PL/SQL block to assign it directly, e.g.
SELECT default_rate
INTO :myblock.rate
FROM app_defaults
WHERE row_key = 1;

Related

ORA-1008 if form variable referenced in ORDER BY Clause

TLDR; Is there anything I can set in an Oracle Form that would let me bind a placeholder to a Data Block's ORDER BY Clause?
I'm developing a form using Oracle Form Builder 10.1.2.3.0 (because it's interfacing with a system that makes other form types undesirable).
It has a Data Block with Query Data Source Type = Table.
Its WHERE Clause allows the user to be flexible in the search, producing rows of varying interest. I want rows with a perfect match to appear before those that are not.
To implement this specification, I wrote the form's WHERE Clause and ORDER BY Clause to reflect this SQL*Plus example:
var sf varchar2(30)
exec :sf := 'X'
with mdual as (
select case when level=1 then dummy else dummy || level end dummy
from dual
connect by level <= 2
)
select *
from mdual
where :sf is null or dummy like '%' || upper(:sf) || '%'
order by case when :sf = dummy then 0 else 1 end asc, dummy;
The form variable reference is not as simple as :sf and the WHERE Clause is a bit more complicated as is the ORDER BY Clause but this type of query is valid. When executed in SQL*Plus, it produces exactly the type of result I desire. You can reverse the first sort expression to prove it.
When I execute the form, I get an ORA-1008 until I comment the first ORDER BY expression.
My conclusion is that Oracle Forms binds placeholder references in a WHERE Clause but not an ORDER BY Clause.
I could experiment with setting the Query Data Source Type to Procedure and pass the procedure the filter field but a view has more utility than a procedure and so I'd prefer to keep using the view that I've defined for the Query Data Source Type.
Is there a way I can coerce Oracle Forms to do what I consider the right thing?
You can use SET_BLOCK_PROPERTY built-in function in order to make it dynamical and depending on a local or bind variable such as
DECLARE
v_orderby := ' CASE WHEN '||:sf||' = ''dummy'' THEN 0 ELSE 1 END, dummy';
BEGIN
SET_BLOCK_PROPERTY('block1',ORDER_BY, v_orderby);
EXECUTE_QUERY;
END;
which might be invoked from a trigger such as WHEN-NEW-BLOCK-INSTANCE after sending cursor to this block by using another action such as clicking on a button or pressing a key such as enter etc.

Oracle APEX cannot save a value into a session

I have a page item P2_ITEM_TYPE_ID that gets set in Pre-Render, using a process calling PL/SQL:
BEGIN
select ITEM_TYPE_ID INTO :P2_ITEM_TYPE_ID from TABLE1 where ITEM_ID = :P2_ITEM_ID;
exception
when no_data_found then
:P2_ITEM_TYPE_ID := null;
END;
Currently the record for that ITEM_ID does not exist in TABLE1 so the exception NO_DATA_FOUND is thrown and originally the ITEM_TYPE_ID is set to null.
Now I want to set the ITEM_TYPE_ID by clicking on one of the cards on the page, grabbing its ID and setting page item P2_ITEM_TYPE_ID to that ID.
I have a dynamic action that runs the following javascript when card is clicked:
var $item_type_id = this.data;
console.log($item_type_id); //prints out correect ID
apex.item("P2_ITEM_TYPE_ID").setValue($item_type_id);
console.log(apex.item("P2_ITEM_TYPE_ID").getValue()); //prints out correct value
//set session state of P2_ITEM_TYPE_ID
apex.server.process ( "SAVE_HIDDEN_VALUE_IN_SESSION_STATE",
{
x01: $item_type_id,
pageItems: "#P2_ITEM_TYPE_ID"
},
{dataType: 'text'} );
I know the code works, as the correct values get printed to the console, but when I check the session of the page, P2_ITEM_TYPE_ID is blank in session. What could be the problem?
It is almost as if something is preventing th value from changing.
I have identical setup on another page with one small difference - the code in the pre-render does not include the exception part because there is always a record in TABLE1 for that ITEM_ID:
BEGIN
select ITEM_TYPE_ID INTO :P3_ITEM_TYPE_ID from TABLE1 where ITEM_ID = :P3_ITEM_ID;
END;
But the rest of the code is identical and session of P3_ITEM_TYPE_ID changes without an issue
You don't need apex.server.process for this, you can post the value of an item to the server with an Execute PL/SQL Code action that follows the javascript action you already have. Put the item name (e.g. P2_ITEM_TYPE_ID) in the Items to Submit attribute (set PL/SQL Code to just null;).
If everything is the same on those pages, except the EXCEPTION part, well - there's a workaround: use an aggregate function, e.g.
select max(ITEM_TYPE_ID) INTO :P2_ITEM_TYPE_ID from TABLE1 where ITEM_ID = :P2_ITEM_ID;
It will prevent the query from returning NO_DATA_FOUND if there's no value for that :P2_ITEM_ID and will store NULL into the :P2_ITEM_TYPE_ID.

Return one variable from a SELECT inside a function

I'm trying to create a function that return a varchar, one of the fields form a select, the aggregation field. I'm getting the next error:
ORA-01422: exact fetch returns more than requested number of rows
What I understand is that the select produce more than one row before aggregating and this triggers the error when trying to put them 'into k, s, categories'
Here is the function:
FUNCTION get_cat(kind_id IN varchar, system_id IN Number) RETURN VARCHAR2
AS categories VARCHAR2(2000);
k STANDARDS.KIND_ID%TYPE;
s SEEDTEST_RESULT.SLRN_ID%TYPE;
BEGIN
SELECT STANDARDS.KIND_ID, SEEDTEST_RESULT.SLRN_ID,
listagg(CAT_LEVEL, ' ' ) within group (order by cat_level)
INTO k, s, categories
FROM STANDARDS, SEEDTEST_RESULT
WHERE STANDARDS.CL_PRIORITY = SEEDTEST_RESULT.CL_PRIORITY
AND SEEDTEST_RESULT.RESULT = 1
AND SEEDTEST_RESULT.TEST_TYPE = 'C'
AND STANDARDS.KIND_ID = trim(kind_id)
AND SEEDTEST_RESULT.SLRN_ID = system_id
GROUP BY STANDARDS.KIND_ID, SEEDTEST_RESULT.SLRN_ID;
RETURN categories;
END get_cat;
The select statement works outside the function when I run it with specific values for kind_id and system_id.
I've been trying to create a temp table so I can get the initial rows from the select and then return categories, but so far I haven't been able to find any helpful information for this particular case. Does anyone knows how can I do this, please?
Thank you.
The problem is with your variable names:
FUNCTION get_cat(kind_id IN varchar, ...
...
AND STANDARDS.KIND_ID = trim(kind_id)
You have a column called kind_id and the query will use that in preference to the PL/SQL variable of the same name.
From the documentation:
If a SQL statement references a name that belongs to both a column and either a local variable or formal parameter, then the column name takes precedence.
So you aren't matching the passed-in value, you're actually finding all rows which match system_id for any value of kind_id. (Except null, and if they have leading/trailing whitespace...)
Change your variable names so they do not clash and there is no confusion. It's common to prefix passed-in argument with, say, a p prefix so you'd be comparing with = p_kind_id, and local variables with an l prefix.
If you really want to keep the names you have, you can also prefix the references to those with the function name:
AND STANDARDS.KIND_ID = trim(get_cat.kind_id)

PL SQL - Stored procedure update column if parameter is not NULL

I've written a stored procedure that updates a table.
But I would like to take into account where one or more of the parameters are NULL.
In such an instance, I don't want to update the column, I want to leave the existing value as is.
I've tried to use:
UPDATE
VS_USER_T
SET
USR_FIRST_NAME = ISNULL(p_NewUsrFName, #p_NewUsrFName)
WHERE
USR_ID = lv_Num_UsrId;
But I get an error on the '#', I'm using Oracle 12c.
This is the procedure call
PROCEDURE UpdateUser
( p_UserId IN VS_USER_T.USR_ID%TYPE,
p_NewUsrFName IN VS_USER_T.USR_FIRST_NAME%TYPE,
p_NewUsrLName IN VS_USER_T.USR_LAST_NAME%TYPE,
p_NewUsrname IN VS_USER_T.USR_LOGIN%TYPE)
Please advise how my UPDATE statement should look like, when 'p_NewUsrname ' can be NULL, in which case I want to leave the existing value as is.
Thanks in advance.
To keep the existing value you need to refer to the existing column value:
USR_FIRST_NAME = ISNULL(p_NewUsrFName, USER_FIRST_NAME)
or you could use:
USR_FIRST_NAME = CASE WHEN p_NewUsrFName is null THEN USER_FIRST_NAME ELSE NewUsrFName END
ISNULL() is not yet a standard Oracle function (at least in the Oracle 12c version that you say you are using). If is of course possible to write a PL/SQL function called ISNULL() and use that.
For a standard Oracle 12c installation, try using NVL or COALESCE instead.
USR_FIRST_NAME = NVL(p_NewUsrFName, USR_FIRST_NAME)
or
USR_FIRST_NAME = COALESCE(p_NewUsrFName, USR_FIRST_NAME)
You could use a decode statement e.g.
update my_table t
set username = decode(p_NewUsrname, NULL, t.username, p_NewUsrname)
where t.id = p_UserId;

Oracle cursor returning all rows in table despite where clause

I am using the following query to return a single value:
select er.elig_code
from idm_elig_rule er
where ER.ROLE_CODE = role_code and ER.SERVICE_CODE = service_code;
When I substitute values for the variables and run it as a single SQL statement it will return just a single value like I want it too. However, when I place the statement in a cursor and loop through the results, it returns all rows in the table. I've also tried a "select into" statement but that still returns all 85 rows in the table. I have a unique index in the idm_elig_rule table that is a combination of the role_code, service_code and another column.
EDIT: Here's how I'm testing it out - using an anonymous block:
declare
role_code_in IDM_ELIG_RULE.ROLE_CODE%type := 'CEMP';
service_code_in IDM_ELIG_RULE.SERVICE_CODE%type := 'PORTL';
cursor get_elig_code is
select ER.ELIG_CODE
from idm_elig_rule er
where ER.ROLE_CODE = role_code_in and ER.SERVICE_CODE = service_code_in;
begin
for r in get_elig_code
loop
DBMS_OUTPUT.PUT_LINE(r.elig_code);
end loop;
end;
EDIT: I changed the names of the variables in the where clause and that resolved the issue.
You have a Name Resolution issue with your variable names since the bind variable names are the same as the column names. Change your variable names to avoid the PL/SQL compiler from deciding for you.
Oracle PL/SQL Variable Name Resolution
Since your variable names are the same as the column names, it is possible that the parser is seeing them as the column names, not as variable, and therefore you are getting all the rows where the columns are equal to themselves, which would only exclude rows with NULL values.
I'd suggest always using different names for your variables than for the columns, to avoid any confusion. A common technique is to use some prefix on the variable name, such as l_ for local variable, p_ for parameters, or g_ for globals.
In this case, it may be better to parameterize the cursor:
CURSOR code_cur (p_role_code INTEGER, p_service_code INTEGER) IS
select er.elig_code
from idm_elig_rule er
where ER.ROLE_CODE = p_role_code and ER.SERVICE_CODE = p_service_code;
Then you would pass values when opening the cursor, e.g.:
FOR code_rec IN code_cur ( my_role_code_value, my_service_code_value ) LOOP
From the limited information in your question i'd say that your parameters are not being instantiated correctly and your select is resolving to:
select er.elig_code
from idm_elig_rule er
where ER.ROLE_CODE = role_code
and ER.SERVICE_CODE = service_code;
Where the DB is seeing role_code and service_code as the columns and not variables so it will always return all rows from your table.
Change the names of your variables to something different from your column names to see if this is the case.

Resources