Apex Collection: how to have actual column names instead of C001, C002 - collections

created a page to allow users to input SQL statement, when user press button, it should Execute this query and another region with Classic Report will display results.
on Button a Dynamic Action execute PL/SQL code below:
on Click event:
begin
IF :P8_YOURSQL IS NOT NULL THEN
APEX_COLLECTION.CREATE_COLLECTION_FROM_QUERY (
p_collection_name => 'SOMECOLLECTION',
p_query => :P8_YOURSQL,
p_truncate_if_exists => 'YES'
);
end if;
end;
Item to Submit: P8_YOURSQL
second Action refreshes CR region and CR region is based on:
SELECT * FROM APEX_COLLECTIONS
WHERE COLLECTION_NAME = 'SOMECOLLECTION';
resulted columns are not limited to the query results and column headers are C001, C002, C003 etc.
(a) how i can limit the columns to the SQL statement contained?
(b) how to change header to the actual column names?
(c) how to check for a valid SQL statement?
please help with sample code How To.

APEX_COLLECTIONS is a generic table so it has a couple of columns for the most common data types with names like C001, N001 etc. As with any table columns can be aliased in a query.
Example:
Create page process before header to populate the collection
APEX_COLLECTION.CREATE_COLLECTION_FROM_QUERY (
p_collection_name => 'EMP_COLLECTION',
p_query => q'!SELECT * FROM emp!',
p_truncate_if_exists => 'YES'
);
Run the page and open the "Session" window from the developer toolbar. Select "Collections" in the "View" dropdown and click "Set". This will list the data in the APEX_COLLECTIONS table for any collections that exist for the session. Take note of the data and the columns the data is in.
Leave the "Sessions" window open and create a sql query on the APEX_COLLECTIONS view with appropriate aliases.
SELECT
c001 as EMPNO,
c002 as ENAME,
c003 as JOB,
c004 as MGR,
<rest_of_columns>...
FROM
APEX_COLLECTIONS WHERE collection_name = 'EMP_COLLECTION'
It's not possible to alias columns when doing a SELECT * FROM ...
If the SELECT * is important then there is another possibility. Create a view on top of APEX_COLLECTIONS with relevant column names. Example:
CREATE OR REPLACE view EMP_COLLECTION_V
AS
SELECT
c001 as EMPNO,
c002 as ENAME,
c003 as JOB,
c004 as MGR
FROM
APEX_COLLECTIONS WHERE collection_name = 'EMP_COLLECTION'
and then use SELECT * FROM EMP_COLLECTION_V as SQL source for the classic report.

To get the columns names for a SELECT * FROM ... have a look at [DBMS_SQL.DESCRIBE_COLUMNS3][https://docs.oracle.com/en/database/oracle/oracle-database/18/arpls/DBMS_SQL.html#GUID-00AB5DE3-C428-4E60-9398-FD4892F32402]. There is an example in the doc that shows how to print the column names.
This can then be implemented in a classic report
Create a page item per column: P1_C1, P1_C2, etc
Set the column header for each column to the corresponding page item: col1 has header &P1_C1, col2 has header &P1_C2, etc
Create a page process to set the column headers based on the sql query using DBMS_SQL.DESCRIBE_COLUMNS3 - OR - use a
Note that it is strongly advised to assert the sql query to avoid unwanted sql (like a delete or drop table command).

Related

Oracle Apex Select List LOV conditional query

I have an Editable Interactive Grid (for product_sale table) with a Select List (to select a product) in Oracle Apex 20.2 app.
Currently I'm using below query to populate this list of values.
SELECT productName,productId FROM product WHERE productAvailability = 'Y'
There are a few products that I need to set the productAvailability as 'N'. When I made this change, Interactive Grid of product_sale shows productId instead of the productName.
What I need to achieve is, only show products with productAvailability = 'Y' for new records (when going to add new record in to the table by clicking Add Row button) and for the old records show the productName of the selected product regardless the productAvailability.
Table Structure
Sample Data
Interactive Grid View
How could I achieve this?
You can achieve this using the "cascading list of values" option in the column settings. Let me illustrate with an example on the EMP sample table - you should be able to translate this to your own code:
Situation: BLAKE can not be selected as manager for any other employees than the one he is already manager for.
This would be the select:
SELECT ename, empno FROM emp WHERE ename != 'BLAKE'
As expected, for records that have BLAKE as manager, the MGR column will display the id because the value is not in the result set of the select list query. This is the behaviour you are seeing.
The solution has 2 steps:
Union the query with a query that has the value of the current row. I have rewritten the query using a CTE.
WITH all_mgr(dv, rv) AS
(SELECT ename, empno FROM emp WHERE ename != 'BLAKE'
UNION
SELECT m.ename, m.empno
FROM emp e
JOIN emp m ON e.mgr = m.empno
WHERE e.ename = :ENAME
)
SELECT dv, rv FROM all_mgr
Make sure column ename from the current row is bind to :ENAME. In "Cascading List Of Values" set both "Parent Column" and "Items to Submit" to ENAME. Now the select list will take the current row value for :ENAME and the list of values will include 'BLAKE' for users that have 'BLAKE' as a manager.

How to implement NOT EXISTS in OPEN QUERY statement in PROGRESS 4GL - OpenEdge 10.2A

I want to create a browse such that it will show all the records from one table if the values of a field do NOT exist in another table.
It is possible to get the records using SQL as:
SELECT myField FROM pub.myTable WHERE
NOT EXISTS (SELECT myField FROM pub.myTable2 WHERE myTable2.myField=myTable.myField)
It is also possible using 4GL as:
FOR EACH myTable WHERE
NOT CAN-FIND(FIRST myTable2 WHERE myTable2.myField=myTable.myField)
The problem is when I put this query in a browse as:
OPEN QUERY myBrowse
FOR EACH myTable WHERE
NOT CAN-FIND(FIRST myTable2 WHERE myTable2.myField=myTable.myField)
it gives an error message
CAN-FIND is invalid within an OPEN QUERY. (3541)
The question is, is it possible to write such an OPEN QUERY statement?
I didn't come up with this, Steve Moore shared it on https://community-archive.progress.com/forums/00026/27143.html
define temp-table ttNoOrder
field field1 as char.
create ttNoOrder.
define query q1 for Customer, Order, ttNoOrder.
open query q1 for each Customer no-lock,
first Order of Customer outer-join no-lock,
first ttNoOrder where not available(Order).
get first q1.
repeat while not query-off-end("q1"):
display Customer.CustNum Customer.Name available(Order).
get next q1.
end.
Works even with dynamic queries:
DEFINE TEMP-TABLE ttNoOrder
FIELD field1 AS CHARACTER .
DEFINE VARIABLE hQuery AS HANDLE NO-UNDO.
CREATE ttNoOrder.
CREATE QUERY hQuery .
hQuery:SET-BUFFERS (BUFFER Customer:HANDLE,
BUFFER Order:HANDLE,
BUFFER ttNoOrder:HANDLE) .
hQuery:QUERY-PREPARE ("for each Customer no-lock, ~
first Order of Customer outer-join no-lock, ~
first ttNoOrder where not available(Order)") .
hQuery:QUERY-OPEN() .
hQuery:GET-FIRST () .
REPEAT WHILE NOT hQuery:QUERY-OFF-END:
DISPLAY Customer.CustNum FORMAT ">>>>>>>>>9" Customer.Name AVAILABLE(Order).
hQuery:GET-NEXT ().
END.

start with prior reference on last select

I have a problem while inserting based on select query
I have a schema in the database with a parent-child relationship that looks like the following
A
B
C
G
L
F
C
G
L
Notice how Element c is reused, because it´s aviable twice with different parent id, but element g is only aviable once, since the id of c is the same in both cases. The select prints everything as expected with the following query
select id,
parent_id,
label
from table
start with parent_id is null
connect by nocycle prior id = parent_id
order siblings by sort
i am having around 2500 elements in this table, but in the end around 4000 are displayed because a few elemnts should be displayed multiple times at different places.
So, to identify both, the first and second g as unique elements, i have written the following insert statement
insert into other_tale (id, parent_id, label)
select create_id new_id,
prior ???,
label,
from table
start with parent_id is null
connect by nocycle prior id = parent_id
order siblings by sort;
Here i am calling a procedure to generate a new id for each raw that has been found. Now i am stuck at the part where i do recieve the new id of the parent element. I know that i can refer to the prior parent_raw in the table beeing select, but am i able to somehow refer to the column new_id of the parent_element in the select?
Create a package with 1 associative array id_cache and 2 functions: f_clear_cache and f_generate_id.
f_clear_cache deletes the cached ids - id_cache.delete.
f_generate_id takes id as argument and returns the new_id
check if the new_id was already generated - id_cache.exists(id)
if not, generate the new_id and cache it - id_cache(id) := new_id
return new_id - return id_cache(id)
finally use the function in your sql statement
insert into other_tale (id, parent_id, label)
select my_package.f_generate_id(id),
my_package.f_generate_id(parent_id),
label
...
note: do not forget to call f_clear_cache when you want to generate new set of ids within the same session.

Can multitable Insert statement combine columns from view and columns from another view?

I'm planning to do SQL expert examination.
I have doubts that answer D is correct:
Evaluate the following command:
CREATE TABLE employees
( employee_id NUMBER(2) PRIMARY KEY
, last_name VARCHAR2(25) NOT NULL
, department_id NUMBER(2)NOT NULL
, job_id VARCHAR2(8)
, salary NUMBER(10,2));
You issue
the following command to create a view that displays the IDs and last
names of the sales staff in the organization:
CREATE OR REPLACE VIEW sales_staff_vu AS
SELECT employee_id, last_name,job_id
FROM employees
WHERE job_id LIKE 'SA_%'
WITH CHECK OPTION;
Which two statements are true regarding the above view? (Choose two.)
A. It allows you to insert rows into the EMPLOYEES table .
B. It allows you to delete details of the existing sales staff from
the EMPLOYEES table.
C. It allows you to update job IDs of the existing sales staff to any
other job ID in the EMPLOYEES table.
D. It allows you to insert IDs, last names, and job IDs of the sales
staff from the view if it is used in multitable INSERT statements.
Source
A is FALSE as the view doesn't allow inserting into the department_id column which is mandatory.
B is TRUE, although it would be more accurate to say that the view only allows deletions of employees where the job_id matches the predicate LIKE 'SA_%'.
C is FALSE, as the WITH CHECK OPTION means that you can't change the job_id if the new job_id doesn't match the view's predicate.
D is FALSE: a multitable insert statement can't just insert some columns into the view and the remaining columns into the employees table. Even if you join the view to the table, the insert must still insert into the base table, not into the view:
insert into
(select e.employee_id, e.last_name, e.department_id, e.job_id
from sales_staff_vu v
join employees e
on v.employee_id = e.employee_id)
values
(1, 'KEMP', 2, 'SA_X');
I suspect this is a test of your ability to verify and ignore wrong information on the internet - i.e. 99% of sites say D is true!
Now, my answer can be easily disproved by crafting a multitable insert statement that successfully inserts via the view.
According to my knowledge answer D is wrong.Reasons are
1.If A is wrong, definitely D is wrong.because department_id column is mandatory field on the table, but it is not mentioned in view.so we can't insert a row using this view.
2.In Answer D,multitable INSERT statements are INSERT ALL, INSERT FIRST,etc.
to check this Answer,i have tried these steps
CREATE TABLE employees123
( employee_id NUMBER(2) PRIMARY KEY
, last_name VARCHAR2(25) NOT NULL
, department_id NUMBER(2)NOT NULL
, job_id VARCHAR2(8)
, salary NUMBER(10,2));
CREATE OR REPLACE VIEW sales_staff_vu123 AS
SELECT employee_id, last_name,job_id,department_id
FROM employees123
WHERE job_id LIKE 'SA_%'
WITH CHECK OPTION;
--department_id is added to view
--here i am trying to insert my employees table rows to employees123 table
INSERT ALL
INTO sales_staff_vu123 --using View
SELECT employee_id, last_name,job_id,department_id
FROM employees;
Error at Command Line:153 Column:15
Error report:
SQL Error: ORA-01702: a view is not appropriate here
01702. 00000 - "a view is not appropriate here"
*Cause:
*Action:
So my decision is we cant use views with multitable insert statements.

MySQL Changing Order Depending On Contents of a Column

I have a MySQL table Page with 2 columns: PageID and OrderByMethod.
I also then have a Data table with lots of columns including PageID (the Page the data is on), DataName, and DataDate.
I want OrderByMethod to have one of three entries: Most Recent Data First, Most Recent Data Last, and Alphabetically.
Is there a way for me to tack an "ORDER BY" clause to the end of this query that will vary its ordering method based on the contents of the "OrderByMethod" column? For example, in this query, I would want to have the ORDER BY clause contain whatever ordering rule is stored in Page 1's OrderByMethod column.
GET * FROM `Data` WHERE `Data`.`PageID`=1 ORDER BY xxxxxx;
Maybe a SELECT clause in the ORDER BY clause? I'm not sure how that would work though.
Thanks!
select Data.*
from Data
inner join Page on (Data.PageID=Page.PageID)
where Data.PageID=1
order by
if(Page.OrderByMethod='Most Recent Data First', now()-DataDate,
if(Page.OrderByMethod='Most Recent Data Last', DataDate-now(), DataName)
);
You can probably do this with the IF syntax to generate a column that you can then order by.
SELECT *, IF(Page.OrderBy = 'Alphabetically', Data.DataName, IF(Page.OrderBy = 'Most Recent Data First', NOW() - Data.DataDate, Data.DataDate - NOW())) AS OrderColumn
FROM Data
INNER JOIN Page ON Data.PageID = Page.PageID
WHERE Page.PageID = 1
ORDER BY OrderColumn
The direction of the ordering is determined in the calculation of the data instead of specifying a direction in the ORDER BY
Can you just append the order by clause to the select statement and rebind the table on postback?
If you want to use the content of the column in Page table as an expression in ORDER BY you have to do it using prepared statements. Let say, you store in OrderByMethod something like "field1 DESC, field2 ASC" and you want this string to be used as it is:
SET #order_by =(SELECT OrderByMethod FROM Page WHERE id = [value]);
SET #qr = CONCAT(your original query,' ORDER BY ', #order_by);
PREPARE stmt FROM #qr;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
If you want the result set to be sorted based on the value of OrderByMethod , you can use IF as it was already mentioned by others, or CASE :
...
ORDER BY
CASE OrderByMethod
WHEN 'val1' THEN field_name1
WHEN 'val2' THEN field_name2
....etc
END

Resources