Oracle APEX select values into classic report rows - oracle

I'm working on an Oracle database-driven web app using apex.There is a P2_ROWS field which contains a list of values selected from the data table, defined in the source sql query. There is a button on the report region, which allows users to sort the list of values in a certain order. A dynamic action is assigned to the button. When the button is clicked, a PL/SQL is executed with a order by query that should change the order in which the rows are displayed.
The source sql is defined as:
select
"PRODUCT_ID",
"PRODUCT_NAME",
"PRODUCT_DESCRIPTION",
"PRICE",
"PRODUCT_LOCATION",
dbms_lob.getlength("THUMBNAIL") as "THUMBNAIL"
from "PRODUCTS"
where
(
instr(upper("PRODUCT_NAME"), upper(nvl(:P2_REPORT_SEARCH, "PRODUCT_NAME"))) > 0
)
The PL/SQL assigned to the button is:
SELECT PRODUCTS.PRODUCT_ID, PRODUCTS.PRODUCT_NAME, PRODUCTS.PRODUCT_DESCRIPTION, PRODUCTS.PRICE, PRODUCTS.PRODUCT_LOCATION, dbms_lob.getlength("THUMBNAIL") as "THUMBNAIL"
INTO :P2_ROWS
FROM PRODUCTS
INNER JOIN TEMP_DISTANCES ON TEMP_DISTANCES.PRODUCT_ID = PRODUCTS.PRODUCT_ID
ORDER BY DISTANCE ASC;
When the button is clicked, the app returns error " ORA-00947: not enough values ORA-06550".
How would you solve this? Please feel free to comment and feel free to suggest any improvement ideas. Thanks.

As #Scott say you trying select many values into one scalar variable. You should define few variables or use concatenation.
SELECT PRODUCTS.PRODUCT_ID, PRODUCTS.PRODUCT_NAME, PRODUCTS.PRODUCT_DESCRIPTION, PRODUCTS.PRICE, PRODUCTS.PRODUCT_LOCATION, dbms_lob.getlength("THUMBNAIL") as "THUMBNAIL"
INTO :P2_PROID_ID, :P2_PROD_NAME, ...
FROM PRODUCTS
INNER JOIN TEMP_DISTANCES
ON TEMP_DISTANCES.PRODUCT_ID = PRODUCTS.PRODUCT_ID
ORDER BY DISTANCE ASC;
or
SELECT PRODUCTS.PRODUCT_ID || ', ' || PRODUCTS.PRODUCT_NAME || ', ' || PRODUCTS.PRODUCT_DESCRIPTION || ', ' || PRODUCTS.PRICE ...
INTO :P2_ROWS
FROM PRODUCTS
INNER JOIN TEMP_DISTANCES
ON TEMP_DISTANCES.PRODUCT_ID = PRODUCTS.PRODUCT_ID
ORDER BY DISTANCE ASC;

Related

Is it possible to evaluate SQL given as text? (Oracle APEX 21.1)

I have created a classic report region (REGION: REPORT_FILTER_SHOP_TYPE).
That has a SQL below this.
SELECT
ID, SHOP_NAME, SHOP_TYPE, OPEN_YEAR, CITY
FROM SHOP_LIST;
I want to apply a filter to this table. The filter criteria will be selected from the list item. And this page have some lists.
For example, if there is no filter, the SQL right above one. But if the "SHOP_TYPE" and "OPEN_YEAR" are selected, execute the SQL below.
SELECT * FROM (
SELECT
ID, SHOP_NAME, SHOP_TYPE, OPEN_YEAR, CITY
FROM SHOP_LIST
) S
WHERE S.SHOP_TYPE = 'BOOKSTORE' AND S.OPEN_YEAR <2010;
I can now create the compose SQL text from selected list items.
What do I need to set to display this result in REPORT_FILTER_SHOP_TYPE?
Well, most probably not like that; why using parameters on a page if you hardcode some values into report's query? Use parameters!
Something like this:
SELECT id,
shop_name,
shop_type,
open_year
FROM shop_list
WHERE ( shop_type = :P1_SHOP_TYPE
OR :P1_SHOP_TYPE IS NULL)
AND ( open_year < :P1_OPEN_YEAR
OR :P1_OPEN_YEAR IS NULL);

Oracle Table Variables

Using Oracle PL/SQL is there a simple equivalent of the following set of T-SQL statements? It seems that everything I am finding is either hopelessly outdated or populates a table data type with no explanation on how to use the result other than writing values to stdout.
declare #tempSites table (siteid int)
insert into #tempSites select siteid from site where state = 'TX'
if 10 > (select COUNT(*) from #tempSites)
begin
insert into #tempSites select siteid from site where state = 'OK'
end
select * from #tempSites ts inner join site on site.siteId = ts.siteId
As #AlexPoole points out in his comment, this is a fairly contrived example.
What I am attempting to do is get all sites that meet a certain set of criteria, and if there are not enough matches, then I am looking to use a different set of criteria.
Oracle doesn't have local temporary tables, and global temporary tables don't look appropriate here.
You could use a common table expression (subquery factoring):
with tempSites (siteId) as (
select siteid
from site
where state = 'TX'
union all
select siteid
from site
where state = 'OK'
and (select count(*) from site where state = 'TX') < 10
)
select s.*
from tempSites ts
join site s on s.siteid = ts.siteid;
That isn't quite the same thing, but gets all the TX IDs, and only includes the OK ones if the count of TX ones - which has to be repeated - is less than 10. The CTE is then joined back to the original table, which all seems a bit wasteful; you're hitting the same table three times.
You could use a subquery directly in a filter instead:
select *
from site
where state = 'TX'
or (state = 'OK'
and (select count(*) from site where state = 'TX') < 10);
but again the TX sites have to be retrieved (or at least counted) a second time.
You can do this with a single hit of the table using an inline view (or CTE if you prefer) with an analytic count - which add the count of TX rows to the columns in the actual table, so you'd probably want to exclude that dummy column from the final result set (but using * is bad practice anyway):
select * -- but list columns, excluding tx_count
from (
select s.*,
count(case when state = 'TX' then state end) over (partition by null) as tx_count
from site s
where s.state in ('TX', 'OK')
)
where state = 'TX'
or (state = 'OK' and tx_count < 10);
From your description of your research it sounds like what you've been looking at involved PL/SQL code populating a collection, which you could still do, but it's probably overkill unless your real situation is much more complicated.

PL/SQL: ORA-30485: missing ORDER BY expression in the window specification

I am compiling this Function but getting ERROR : missing ORDER BY expression in the window specification
CREATE OR REPLACE FUNCTION WFir_get_act_section_cd(firnum IN NUMBER,langcd IN NUMBER) RETURN VARCHAR2
as
ACTSEC VARCHAR2(1500);
BEGIN
begin
--- ERROR START
select ltrim(max(sys_connect_by_path(NVL(act_long,' ') || '/' || NVL(section,' '),',')),',') as FIR_ACT_SEC into ACTSEC from(select NVL(act_long,' ') || '/' || NVL(section,' '), row_number() over() rn from rep_fir_sections sec
INNER JOIN m_act a on sec.act_cd = a.act_cd
INNER JOIN m_section c on sec.section_cd = c.section_code
and FIR_REG_NUM = FIRNum
and a.lang_cd = langcd) WHERE ROWNUM <=1 start with rn = 1
connect by prior rn = rn -1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
end;
return ACTSEC;
END;
When we are compiling above function after migrate this function form MYSQL to Oracle i am getting error "missing ORDER BY expression in the window specification"
I don't know why i am getting this ERROR, Please help me to resolve this ERROR
Row_number() over () is incorrect.
order by is REQUIRED for row_number to work.
Row_number() over (order by Null /*or you decide what field list*/) .
To assign a row_number you have to specify order by some column. The system doesn't know what row to assign to #1 if you don't specify an order for the system to put them into. Even order by null or order by 1 should work; but you'll probably want a specific field or fields to order by.
Put another way, order by isn't optional on a Row_number window function.
Doc Link
"...
ROW_NUMBER is an analytic function. It assigns a unique number to each row to which it is applied (either each row in the partition or each row returned by the query), in the ordered sequence of rows specified in the order_by_clause, beginning with 1. ..."
This implies without an order by, no row number can be assigned.

Recursive records oracle

I'm currently trying to create a report using SQL Developer.
I have these 2 tables:
PERSONS (IDPerson, NamePerson)
PENALTIES (IDPenalty, DatePenalty, Description, IDPerson)
The tables are polulated.
How could I create a table like
using recursive queries in SQL? or it's there any other solution?
Thank you in advance.
select p.nameperson as name, p.idperson as id,
listagg(to_date(x.datepenalty, 'dd/mm/yyyy') || ' - ' || x.description, '; ')
within group (order by x.datepenalty) as penalties
from persons p left outer join penalties x
on p.idperson = x.idperson
group by p.idperson;
(Not tested - you didn't provide test data.)

prevent full table scan

I have following query:
select id,
c1,
c2,
c3
from tbl t1
join
(select id
from tbl t2
where upper(replace(c5, ' ', '')) like upper(?)
) j
on j.id = t1.id
? is some wildcard parameter string like %test%.
c5 column has index on the function used to access it:
create index tbl_c5_idx on tbl(upper(replace(c5, ' ', '')))
When I run just inner query it uses tbl_c5_idx, however when I run the whole query it turns into full table scan which is much slower.
Are there any way to avoid full table scans? Hints or rewrite join condition. I can not rewrite whole query as inner query is constructed dynamically depending on the input conditions.
A very basic example to test your functionality
create table test(id number,value varchar2(200));
insert into test values(1,'gaurav is bad guy');
insert into test values(2,'gaurav is good guy');
SELECT *
FROM test
WHERE UPPER (REPLACE (VALUE, ' ', '')) LIKE UPPER ('%gauravisbad%');
before creating index this is doing a full table scan for obvious reason ,because no index get created.
create index tbl_c5_idx on test(upper(replace(value, ' ', '')));
The reason why i am asking you to avoid inner join on the same table because you're using the table twice once to get your records from your filter condition where your index are used and then join on the basis of id which is preventing of using index ,because you dont have index on id column,this can be done with a simple filter condition.
Please let me know if you're again finding out the same issue of full table scan ,or you're not getting the same result from this query .
if you're running the subquery only, it doesn't use the id column in the filters the way the parent query does, therefore the index can be used. In the parent query you are using the id as well, which prevents the index from being used. Maybe adding an index on (id, upper(replace(c5, ' ', ''))) would solve the problem.
Gaurav Soni is right: you don't need a subquery to achieve your goal.
always check performances rather than the explain plan. Performances might just be worst with your hint than without. Oracle is NOT stupid.
Seems I found solution, or at least a thing that helps.
I used index hint, so access is done with tbl_c5_idx.
That is how final query looks now:
select /*+ index(t1) */ id,
c1,
c2,
c3
from tbl t1
join
(select id
from tbl t2
where upper(replace(c5, ' ', '')) like upper(?)
) j
on j.id = t1.id

Resources