ORA-01403 when referencing page item in PL/SQL - oracle

I am building an Oracle ApEx page to show charts of data. The data are entered on to a table as inputs, but the actual calculated metric can be different depending on what KPI is being viewed (AVG(VALUE_1); SUM(VALUE_1)/SUM(VALUE_2); etc.); these calculations are stored on a separate table. Since I want everything to be dynamic for use interactions, I am using a source of PL/SQL Function Body returning SQL Query. Here is a simplified version
DECLARE
SQL_STMT VARCHAR2 (32767);
CALC_CLAUSE VARCHAR2 (4000);
BEGIN
SELECT CALCULATION
INTO CALC_CLAUSE
FROM KPI_TYPES
WHERE ID = 101;
--when the value itself is entered, the results display as intended
SQL_STMT := 'SELECT DATE, ' || CALC_CLAUSE || ' KPI_VALUE
FROM DATA_VALUES
WHERE TYPE_ID = :P_KPI
GROUP BY DATE';
RETURN SQL_STMT;
END;
As noted above, this functions as expected when the KPI's ID is manually entered, but I want this to be dynamic. I have a select list page item (P_KPI) for the user to choose the viewed KPI. Its value is actually set from a previous page with validation, so it should never be null.
When I update the CALC_CLAUSE condition:
WHERE ID = :P_KPI
I receive the following error:
ORA-01403: no data found
How can I reference this page item in the function?

Code you posted is invalid (wouldn't compile) and explanation you gave is wrong. Although it is a simplified version, I'd prefer if you posted accurate information.
You said that SQL statement is
SELECT DATE, '||CALC_CLAUSE||' KPI_VALUE
FROM DATA_VALUES ...
while CALC_CLAUSE looks like
WHERE ID = :P_KPI
so the whole query looks like
SELECT DATE, WHERE ID = :P_KPI KPI_VALUE
FROM DATA_VALUES ...
which just doesn't make sense. What is that CALC_CLAUSE, after all? Shouldn't it be AVG(VALUE_1) or something like that?
As of :P_KPI item whose value seems to be NULL: it doesn't matter that you see it on the screen - it should be in the session state. The simplest way to do that is to submit the page, so - did you do it?
Try to create a stored function (in the database, not in Apex) and pass it P_KPI value as a parameter. Then test what you get, as well as the result. When working with dynamic SQL, it is a good idea to DBMS_OUTPUT.PUT_LINE the resulting statement (that would be SQL_STMT in your example), copy/paste it into SQL*Plus (or any other tool you use, such as SQL Developer) and see whether it works correctly.
For example:
CREATE OR REPLACE FUNCTION f_stmt (par_kpi IN NUMBER)
RETURN VARCHAR2
IS
sql_stmt VARCHAR2 (32767);
calc_clause VARCHAR2 (4000);
BEGIN
SELECT calculation
INTO calc_clause
FROM kpi_types
WHERE id = par_kpi;
sql_stmt :=
'SELECT DATE, '
|| calc_clause
|| ' KPI_VALUE '
|| ' FROM DATA_VALUES '
|| ' WHERE TYPE_ID = '
|| par_kpi
|| ' GROUP BY DATE';
DBMS_OUTPUT.put_line (sql_stmt);
RETURN sql_stmt;
END;

Starting with the stored function suggested by Littlefoot, I was able to use this solution:
http://vincentdeelen.blogspot.com/2014/02/interactive-report-based-on-dynamic-sql.html
I have a page process that creates a collection with the dynamic query string. The charts region SQL now queries from the collection, and I can use all of my page items to filter the data appropriately.

Related

How to Get datablock default where that oracle forms generate with Enter-query action

I am working with oracle forms 6i.
Simply I am using database block with these items :
Employees: employee_id , job_id , department_id , manager_id
note: employees is the data-block name .
For example: when end user click enter-query button and write 50 in department_id item and then click execute-query button; data block will return all employees who are in department 50.
My question is : How can I get the WHERE CLAUSE that oracle forms generate when returned desired data?...
I used this code in pre-query trigger
:parameters.whr:=get_block_property('employees',default_where);
But it returned no results
You're close, but not close enough. It is the GET_BLOCK_PROPERTY you need, but use its LAST_QUERY parameter. It will return SQL statement of the last query in the specified block.
Alternatively, use system variable :SYSTEM.LAST_QUERY (returns the same result).
Here's an example( the following code might be put in KEY-EXEQRY trigger of employees block ):
declare
l_lastq varchar2(4000);
l_where_position number;
l_where_clause varchar2(4000);
begin
execute_query;
l_lastq := :system.last_query;
l_where_position := instr(lower(l_lastq), 'where');
if l_where_position > 0 then
l_where_clause := substr(l_lastq, l_where_position, length(l_lastq));
message('WHERE clause: ' || l_where_clause);
end if;
end;

Oracle Function required with certain logic to implement in Spotfire

I have created a Function from Oralce DB and used it as input to Spotfire report. The reason why i used a function instead of view is because the parameters had some complex logic operations and i was not able to get that done in a view.
Coming to the report i have built. Currently i am using 2 parameters in the function and i am taking both the values from Spotfire text area in Data on Demand mode. Issue is that unless i enter values for both parameters i wont get the output. My requirement is that i need to add few more parameters for the report which i can but i need to set up the function and the settings in Spotfire such that if 5 parameters are there , if users enters one value for just one parameter report should run for that parameter. So the Functions needs to be in such a way that if value is entered that should be taken and if its left empty then that should not be considered. leaving the Spotfire part if the Function is built with the specifics mentioned by me i can implement it directly.
I have got different solutions from everywhere and i am not able to implement anything properly. I am updating all the examples and need help in figuring out the right one and correcting it or to do it in a completely different manner
Code Type 1:
create or replace function Function_test(p1 varchar2='',p2 varchar2='',p3 varchar2)
return SYS_REFCURSOR as
my_cursor SYS_REFCURSOR;
begin
open my_cursor for
select distinct
x.c1
x.c2
x.c3
from x
where x.c1=p3
and (p1='' or x.c2=p1)
and (p2='' or x.c3=p2);
return my_cursor;
end;
The above code seems to be an example from MSSQL and i am able to get the logic but dont know the right way to implement in Oracle. When i tried i just got lot of errors.
Code Type 2:
create or replace function Function_delete(param1 Varchar2, param2 varchar2)
RETURN Varchar2 IS
ssql varchar2(3000);
test varchar2(1000);
begin
ssql := 'select col1,col2 from table_x';
if param1 is null and param2 is not null then
ssql := ssql || ' Where col2='''|| param2 ||'''';
end if;
if param1 is not null and param2 is null then
ssql := ssql || ' Where col3= ''' || param1 ||'''';
end if;
if param1 is not null and param2 is not null then
ssql := ssql || ' Where col3 = ''' || param1 || ''' and col2='''|| param2 ||'''';
end if;
dbms_output.put_line(ssql);
execute immediate ssql into test;
return test;
--EXCEPTION
-- WHEN OTHERS THEN
-- return 'Hello';
end Function_delete;
In the above example i am not able to implement the logic right in Spotfire cos it requires columns to get the data. Ultimately i need a code that accepts no of parameters that are given by the user rather than working only when all parameters are given. It needs to have columns visibly displayed since that way i can implement the same in Spotfire Reports.
you can try this:
create or replace function Function_test(p1 in varchar2,p2 in varchar2,p3 in varchar2)
return SYS_REFCURSOR as
my_cursor SYS_REFCURSOR;
begin
open my_cursor for
select
1
from
dual
where 1=1
and '1'=p1
and '2'=p2
and '3'=p3 ;
return my_cursor;
end;

Oracle dynamic parameters

I'm struggling to create a dynamic sql parametrized query. It involves using 'IS NULL' or 'IS NOT NULL'
Here's a simple pl/sql query:
CREATE OR REPLACE PROCEDURE GET_ALL_INFORMATION
(
"PARAM_START_DATE" IN DATE,
"PARAM_END_DATE" IN DATE,
"PARAM_IS_SUBMITTED" IN NUMBER,
"EXTRACT_SUBMITTED_CONTACTS" OUT sys_refcursor
) IS
sql_stmt VARCHAR2(3000);
PARAM_CONDITION VARCHAR2(20);
BEGIN
IF PARAM_IS_SUBMITTED = 1 THEN
PARAM_CONDITION := 'NOT NULL';
ELSE
PARAM_CONDITION := 'NULL';
END IF;
sql_stmt := ' SELECT
REGISTRATION_NUMBER,
NAME PROVIDER_TYPE,
ORGANIZATION
FROM TABLE_A
WHERE
P.DATE_FINALIZED IS :A;
OPEN EXTRACT_SUBMITTED_CONTACTS FOR sql_stmt USING PARAM_CONDITION;
Whereas the parameter (:A) in (USING PARAM_CONDITION) should have 'NULL' or 'NOT NULL'. It does not seem to work the way I envisioned.
Am I missing something?
As explained by GriffeyDog in a comment above, bind parameters could only be used as place holder for values. Not to replace keywords or identifiers.
However, this is not really an issue here, as you are using dynamic SQL. The key idea ifs that you build your query as a string -- and it will be parsed at run-time by the PL/SQL engine when you invoke EXECUTE or OPEN .. FOR.
Simply said, you need a concatenation -- not a bound parameter:
...
sql_stmt := ' SELECT
REGISTRATION_NUMBER,
NAME PROVIDER_TYPE,
ORGANIZATION
FROM TABLE_A
WHERE
P.DATE_FINALIZED IS ' || PARAM_CONDITION;
-- ^^
OPEN EXTRACT_SUBMITTED_CONTACTS FOR sql_stmt;

PL/SQL - How to use an array in an IN Clause

I'm trying to use an array of input values to my procedure in an IN Clause as part of the where clause of a cursor. I know that this has been asked before, but I haven't seen how to make my syntax compile correctly.
In the package specification, the type is
TYPE t_brth_dt IS TABLE OF sourceTable.stdt_brth_dt%TYPE INDEX BY PLS_INTEGER;
sourceTable.std_brth_dt is a date column in the table.
Simplified version of my cursor is in the package body is -
cursor DataCursor_Sort( p_brth_dt in t_brth_dt) is
SELECT *
FROM sourceTable
WHERE a.brth_dt IN (select column_value
from table(p_brth_dt))
When I try to compile this, I'm getting the following errors.
[1]:(Error): PLS-00382: expression is of wrong type
[2]:(Error): PL/SQL: ORA-22905: cannot access rows from a non-nested table item
I know this looks similar to other questions, but I don't understand what the syntax error is.
In order to use collection defined as a nested table or an associative array in the from clause of a query you either should, as #Alex Poole correctly pointed out, create a schema level (SQL) type or use one, that is available to you trough ODCIConst package - odcidatelist as you intend to use a list of dates. For example, your cursor definition might look like this:
cursor DataCursor_Sort(p_brth_dt in sys.odcidatelist) is
select *
from sourceTable
where a.brth_dt IN (select column_value
from table(p_brth_dt))
OR
cursor DataCursor_Sort(p_brth_dt in sys.odcidatelist) is
select s.*
from sourceTable s
join table(p_brth_dt) t
on (s.brth_dt = t.column_value)
Note: You should take into consideration the time part of a date when performing a date comparison. If you want to compare date part only it probably would be useful to get rid of time part by using trunc() function.
It is possible to use a PL/SQL-defined nested table type (as opposed to a SQL-defined nested table type) indirectly in an IN clause of a SELECT statement in a PL/SQL package. You must use a PIPELINED function as an intermediary. It felt kind of clever to write, but I don't believe in its fundamental usefulness.
CREATE OR REPLACE PACKAGE so18989249 IS
TYPE date_plsql_nested_table_type IS TABLE OF DATE;
dates date_plsql_nested_table_type;
FUNCTION dates_pipelined RETURN date_plsql_nested_table_type PIPELINED;
PROCEDURE use_plsql_nested_table_type;
END so18989249;
/
CREATE OR REPLACE PACKAGE BODY so18989249 IS
FUNCTION dates_pipelined RETURN date_plsql_nested_table_type
PIPELINED IS
BEGIN
IF (dates.count > 0)
THEN
FOR i IN dates.first .. dates.last
LOOP
IF (dates.exists(i))
THEN
PIPE ROW(dates(i));
END IF;
END LOOP;
END IF;
END;
PROCEDURE use_plsql_nested_table_type IS
BEGIN
dates := NEW date_plsql_nested_table_type();
-- tweak these values as you see fit to produce the dbms_output results you want
dates.extend(5);
dates(1) := DATE '2013-12-25';
dates(2) := DATE '2013-01-01';
dates(3) := DATE '2013-07-01';
dates(4) := DATE '2013-09-03';
dates(5) := DATE '2008-11-18';
FOR i IN (SELECT o.owner,
o.object_name,
o.object_type,
to_char(o.last_ddl_time, 'YYYY-MM-DD') AS last_ddl
FROM all_objects o
WHERE trunc(o.last_ddl_time) IN
(SELECT column_value FROM TABLE(dates_pipelined))
--uses pipeline function which uses pl/sql-defined nested table
)
LOOP
dbms_output.put_line('"' || i.owner || '"."' || i.object_name || '" ("' || i.object_type || ') on ' || i.last_ddl);
END LOOP;
END;
END so18989249;
/
begin so18989249.use_plsql_nested_table_type; end;
/
The type has to be created at SQL level, not in a package. An SQL query doesn't know how to use any types defined in PL/SQL. So you'd have to do:
CREATE OR REPLACE TYPE t_brth_dt IS TABLE OF date;
/
... and remove the type from your package specification. (Or give them different names, at least, and they won't be interchangeable in use). Because it's at SQL level, you also can't use sourceTable.stdt_brth_dt%TYPE in the declaration, unfortunately.

Searching data from table by passing table name as a parameter in PL/SQL

Is this stored procedure in oracle is correct for searching data from table by passing table name as a parameter
CREATE OR REPLACE PROCEDURE bank_search_sp
(
p_tablename IN VARCHAR2,
p_searchname IN VARCHAR2,
p_bankcode OUT VARCHAR2,
p_bankname OUT VARCHAR2,
p_dist_code OUT NUMBER
)
AS
v_tem VARCHAR2(5000);
BEGIN
v_tem := 'SELECT bankcode,bankname,dist_code FROM ' || UPPER (p_tablename) || '
WHERE bankname LIKE '''|| p_searchname||'''';
EXECUTE IMMEDIATE v_tem
INTO p_bankcode,p_bankname,p_dist_code
USING p_searchname ;
END bank_search_sp;
If you need this procedure, then I guess that you have several tables with the columns bankcode, bankname and dist_code. If this is true, then try to normalize your model if possible.
The USING term is the correct approach, but you have to use the parameter in your query.
To avoid SQL injection, you could use dbms_assert.sql_object_name.
This should work for you:
v_tem := 'SELECT bankcode, bankname, dist_code FROM '
|| dbms_assert.sql_object_name(p_tablename)
|| ' WHERE bankname LIKE :1';
Your EXECUTE IMMEDIATE will throw an exception when finding no row or more than one row, so using LIKE might not be a good idea.
Questions that you should ask yourself:
Is the model properly normalized?
Do you really need to use LIKE, or is = what you want?
If you want to use LIKE, how should the program deal with NO_DATA_FOUND / TOO_MANY_ROWS exceptions?

Resources