Oracle apex, selecting item from dropdown causes infinite buffering - oracle

I have created a list of values in shared components, using the query:
SELECT CONCAT(First_Name, Last_Name) as display_value,
Customer_ID as return_value
FROM tbl_Customer;
However, on opening the form and selecting an item from the dropdown,multiple buffering icons appear next to the field and it becomes unusable. I have also entered the query manually, rather than using a shared component which gave the same result, and I am lost on what to try next. Any suggestions would be extremely helpful.
Testing the query on its own, it finds the display and return values as expected
Edit:
After checking the debug menu, I am seeing this:
The query seems to be executing but something is going wrong, yet not sending an error code.
Edit 2: Once again I have edited the query to improve the spacing and make the display value less ambiguous and the same issue is still occuring, which makes me wonder if this is an issue with apex rather than a code fault as the query executes perfectly on its own.
SELECT (Customer_ID || ' ' || First_Name || ' ' || Last_name) as display_value,
Customer_ID as return_value
FROM tbl_Customer;

Related

Showing converted Base64 (from hex) in an existing SQL Server 2019 view

I do voluntary work at an animal shelter. We have an application which uses a SQL Server 2019 database. I have created a view that includes a varbinary(max) column. The value in this column is a picture, stored in hexadecimal-format. I would like to convert this Hex-value to a base64-binary file and add these to the view as an extra column.
I found the perfect solution for my situation in SQL Server : hex to base64. The example provided converts 1 single hex-value into 1 base64-value. I now need to add this solution to my view, but I'm not having any success.
The offered solution:
DECLARE #TestBinHex varchar(max), #TestBinary varbinary(max), #Statement nvarchar(max);
SELECT #TestBinHex = '0x012345';
SELECT #Statement = N'SELECT #binaryResult = ' + #TestBinHex;
EXECUTE sp_executesql #Statement, N'#binaryResult varbinary(max) OUTPUT', #binaryResult=#TestBinary OUTPUT;
SELECT
CAST(N'' AS XML).value(
'xs:base64Binary(xs:hexBinary(sql:column("bin")))'
, 'VARCHAR(MAX)'
) Base64Encoding
FROM
(SELECT #TestBinary AS bin) AS bin_sql_server_temp;
A simplified version of my view:
SELECT
a.cat_id, a.catname, s.cat_id,
s.stay_id, s.shelter_handler, s.shelter_kennel, s.picture
FROM
dbo.animal AS a
OUTER APPLY
(SELECT TOP 1 *
FROM dbo.shelterdata
WHERE a.cat_id = s.cat_id
ORDER BY s.stay_id DESC) AS S
WHERE
(a.cat_id IS NOT NULL) AND (s.leave_date IS NULL)
The view shows an overview of all cats currently present in the shelter (leave_date is NULL). The reason for the TOP 1 is that sometimes shelter animals get returned, and the application then assigns a new stay_id. To prevent duplicate values from the join, I only return the value of the most recent stay_id.
What I am trying to achieve: the second table (dbo.shelterdata) includes the picture, stored in hex value. I'd like to add a column Base64Encoding to the view which includes the converted value.
My attempts
I was successful in replacing the static value '0x012345' by a SELECT statement. But the way the solution is formatted, it only allows for one input value. So I had to restrict it with a WHERE clause. It is obvious to me that I need to make a subquery which inputs the hex value based on the unique cat_id. However, it has been many years since I worked with variable, so I'm struggling with the formatting of the statement.
My request
Does anyone have a suggestion how to build the conversion into the view?
Any assistance would be greatly appreciated.
After searching for a few more hours, I stumbled onto the solution. Maybe it will help someone else in the future. The solution is remarkably simple, as is often the case.
My view, mentioned above, is called dbo.shelter_view
select sv.picture,sv.cat_id,
cast('' as xml).value(
'xs:base64Binary(sql:column("sv.picture"))', 'varchar(max)'
) as Base64Encoding
from dbo.shelter_view as SV

Trying to use EXECUTE IMMEDIATE, cannot compile procedure

Trying to write a procedure which takes no values in, adds a sale price column to my existing product table, then loops through to calculate a sale price and insert that into the new column.
I haven't been able to get anything to work, I think it's something to do with Oracle not liking ALTER TABLE to be run from inside a procedure, but I don't know, and I don't know enough to direct my attempts anywhere else.
This is my attempt
CREATE or REPLACE PROCEDURE ProductLineSale as
BEGIN
DECLARE
NewSalePrice NUMBER(6,2):=0;
EXECUTE IMMEDIATE 'alter table ' || Product || 'add or replace column' || 'SalePrice NUMBER(6,2);'
FOR p in (SELECT ProductStandardPrice FROM Product
group by ProductStandardPrice)
LOOP
CASE WHEN p.ProductStandardPrice>=400 THEN NewSalePrice:=.9*price
WHEN p.ProductStandardPrice<400 THEN NewSalePrice:=.85*price
INSERT INTO Product(SalePrice)
VALUES(NewSalePrice)
END LOOP;
END ProductLineSale
Product is the literal name of the Product table in my database. SalePrice is what I would like the new column to be named.
SQLDeveloper won't compile the procedure. The error I get is fairly cryptic as well:
Error(2,10): PLS-00103: Encountered the symbol "=" when expecting one of the following: constant exception table long double ref char time timestamp interval date binary national character nchar.
There are a host of errors... The ones that jump out at me on a first pass.
The requirement doesn't make sense. Adding a column in a procedure doesn't make sense. You create a procedure because you want code to be reusable. Adding a column can only be done once, hence it is by definition not reusable.
A procedure has to be compiled before it can be executed. If there is a reference to a column that doesn't exist, the procedure will fail to compile. Thus, if you want to add a column to the table using dynamic SQL, all subsequent references to the column (i.e. your insert statement) would need to use dynamic SQL as well.
Your DDL statement is incorrect. There is no add or replace clause, it's alter table product add SalePrice NUMBER(6,2). Note that when you're building your string, you also have to ensure that there is a space between the clause add and the column name SalesPrice-- one of the two strings you're concatenating together would need that.
It doesn't make sense to have a declare where you do. You can declare variables between the as and the begin one line above. You are allowed to create a nested PL/SQL block there with the declare but then you'd need a matching begin and end that you don't have.
If you're going to use a case statement in PL/SQL, you'd need an end case. You would also need to have a semicolon ; after each expression.
Your insert statement is also missing a semicolon.
Logically, I am hard pressed to imagine that you really want have an insert here. It doesn't make logical sense to create a bunch of new rows in the table when you add a new column. I would assume that you want to update the value of the new column in existing rows. Which, presumably, requires that your cursor selects the primary key column(s) and potentially changes whether and what you're grouping by.
Product and price are being used as local variables in the execute immediate statement and in the case statement but aren't defined. I'm guessing that you just want to hard code the name of the table you're altering and that price is supposed to reference the name of a column in the table that you need to select in your cursor but I'm not sure.
This case statement is syntactically valid (or would be if price resolves to something valid). Many of the other corrections are less obvious because of the reasons I detailed above.
case when p.ProductStandardPrice>=400
then NewSalePrice:=.9*price;
when p.ProductStandardPrice<400
THEN NewSalePrice:=.85*price;
end case;
If I was to speculate at what you actually want (given that this is a homework assignment with requirements that don't actually make sense), I'd guess something like
CREATE or REPLACE PROCEDURE ProductLineSale
as
begin
execute immediate 'alter table Product add SalePrice NUMBER(6,2)';
execute immediate 'update product ' ||
' set SalePrice = (case when ProductStandardPrice >= 400 ' ||
' then 0.9 * Price ' ||
' else 0.85 * Price ' ||
' end) ';
end ProductLineSale;
If you're going to use dynamic SQL, it almost always makes sense to declare a local variable, build the SQL statement in that variable, and then execute it so that you can debug things by printing out the statement you've build to debug it.

Making a search bar manually WITHOUT interactive report. (Oracle APEX)

I am trying to make an application completely manually without using any interactive reports or generated PL/SQL. Everything is working at the moment but I am stumped when it comes to the search bar and I can't find anything online to help me.
I have a classic report called 'Browse Job Vacancies' and a 'Search' button; I also have the 'C1_JOB_TITLE_ITEM' search bar and a page process called 'Search'.
In the process I have this code:
SELECT
JOB_CODE,
JOB_TITLE,
JOB_DESCRIPTION,
SITE_NAME,
EMAIL_ADDRESS,
TELEPHONE_NUMBER,
SALARY,
START_OF_PLACEMENT,
APPLICATION_METHOD,
APPLICATION_CLOSING_DATE
FROM JOB
WHERE upper(job_title) = upper(:C1_JOB_TITLE_ITEM);
I am getting this error:
ORA-06550: line 1, column 64: PLS-00428: an INTO clause is expected in this SELECT statement
So I created this code:
DECLARE temp_row char;
BEGIN
SELECT
JOB_CODE,
JOB_TITLE,
JOB_DESCRIPTION,
SITE_NAME,
EMAIL_ADDRESS,
TELEPHONE_NUMBER,
SALARY,
START_OF_PLACEMENT,
APPLICATION_METHOD,
APPLICATION_CLOSING_DATE
INTO temp_row
FROM JOB
WHERE job_title = :C1_JOB_TITLE_ITEM;
END;
Here I am getting this error:
ORA-06550: line 13, column 16: PL/SQL: ORA-00947: not enough values
I am completely stumped on what to do at this point so any help at all is greatly appreciated. Sorry if I'm not being detailed enough this is my first time writing in Stack Overflow.
(Editing because someone didn't think this was an answer.)
You say you have a page process. You don't need any sort of process for this. You should have a Region with the following properties (assuming 18.x):
Type = Classic Report
Source > Location = Local Database
Source > Type = SQL Query
Source > SQL Query > your query from your first block above
Apex will run the query and create a report based on it. Processes are for PL/SQL blocks that do something like populating data or manipulating the database in some way.
When you enter a keyword in your item and click your button (the button action should be Submit Page), Apex will populate C1_JOB_TITLE_ITEM in session state with the user's value, then use that in the bind variable when it re-generates the page.
1) Create a classic report with that SQL as the source code.
2) Set C1_JOB_TITLE_ITEM in Page Items To Submit attribute, just under region source.
3) Create the page item C1_JOB_TITLE_ITEM, as your search field.
4) Create a Dynamic Action on Change of that item, and refresh your classic report region. The timing of this refresh is up to you. Step 2 ensures the value you've typed in the browser is sent to the database for the purpose of re-running that query.
Do you have a function based index on upper(job_title)? Otherwise you may experience performance issues.

PL/SQL: ORA 01422 fetch returns more than requested number of rows

I am developing an order transaction where a user can order a product. Once they clicked the 'add to cart' button, it will be able to save on the database in how many times they want with the same order id. Order id is like a transaction id.
My problem is that whenever I want to display the items that customer ordered, it displays an error or ORA 01422. How can I resolve this error?
Here is my code
DECLARE
order_item_id NUMBER;
BEGIN
order_item_id := :MOTOR_PRODUCTS_ORDER.M_ORDERID;
SELECT MOTOR_ID,
MOTOR_QTY_PURCHASED,
UNITPRICE
INTO :MOTOR_ORDER_ITEMS.MOTOR_ID,
:MOTOR_ORDER_ITEMS.MOTOR_QTY_PURCHASED,
:MOTOR_ORDER_ITEMS.UNITPRICE
FROM MOTOR_ORDERS
WHERE motor_order_id = order_item_id;
END;
As krokodilo says, this error is caused because your query returns multiple rows. Depending on what you want to do, you have a couple of options.
If you want multiple values then either use a loop and process them one row at a time (if you are going to be performing a dml operation use bulk collect). If you only want a single row then narrow your result set down with an extra where clause, or use MAX to ensure you only get one value back.
If there is more than one row which will be returned from a query you'll need to use a cursor. One way to do this is with a cursor FOR loop:
DECLARE
order_item_id NUMBER;
BEGIN
order_item_id := :MOTOR_PRODUCTS_ORDER.M_ORDERID;
FOR aRow IN (SELECT MOTOR_ID, MOTOR_QTY_PURCHASED, UNITPRICE
FROM MOTOR_ORDERS
WHERE motor_order_id = order_item_id)
LOOP
-- Do something here with the values in 'aRow'. For example, you
-- might print them out:
DBMS_OUTPUT.PUT_LINE('MOTOR_ID=' || aRow.MOTOR_ID ||
' MOTOR_QTY_PURCHASED=' || aRow.MOTOR_QTY_PURCHASED ||
' UNITPRICE=' || aRow.UNITPRICE);
END LOOP;
END;
Best of luck.
This looks like a Forms question; is it? If so, my suggestion is to let Forms do that job for you.
According to what you posted, there are two blocks:
MOTOR_PRODUCTS_ORDER (a master block, form type)
MOTOR_ORDER_ITEMS (a detail block, tabular type)
I guess that there is a master-detail relationship between them. If there's none, I'd suggest you to create it. Although you can make it work without such a relationship, it'll be much more difficult. If you are unsure of how to do it, start from scratch:
delete MOTOR_ORDER_ITEMS block (detail)
create it once again, this time by following the Data Block Wizard
Set MOTOR_PRODUCTS_ORDER to be its master block
Relationship is on ORDER_ID column/item
Let's presume that by this point everything is set up. Retrieving items that belong to that ORDER_ID is now very simple:
navigate to master block
enter query mode
enter value into an item that represents ORDER_ID
execute query
End of story. Forms triggers & procedures (which were created by the Wizard) will do its job and retrieve both master and detail records.
No need for additional coding; if you're skilled developer, you can create such a form in a matter of minutes. Won't be beautiful, but will be effective & reliable.
There's really no use in doing it manually, although it is possible. Your code works if there's a single item for that ORDER_ID. For two or more items, as you already know, it'll fail with TOO-MANY-ROWS error.
Basically, if you insist, you should use a loop:
you'd enter ORDER_ID into the master block
as you need to move through the detail block, i.e. use NEXT_RECORD, which is a restricted procedure, you can't use number of triggers (open Forms Online Help System and read about them) so a "Show items" button (with its WHEN-BUTTON-PRESSED trigger) might be just fine
a cursor FOR loop would be your choice
for every row it fetches, you'd populate block items and
navigate to next record (otherwise, you'd keep overwriting existing values in the 1st tabular block row)
As I said: possible, but not recommended.

Dynamic SQL, comparing two records one column at a time

Scenario: we have flashback set up on certain tables in a Oracle database. Every now and then, we want to see what fields changed from one row to another. We can inspect visually of course but that is error-prone.
So I had the "brilliant" idea to try to step through the rows, store the current record into one record variable, and the prior record into another one. Then, field-by-field, compare each field, and if different, print out the field name and the values. Something like this:
DECLARE CURSOR myflash IS SELECT * FROM myflashtable;
OLDRECORD myflashtable%ROWTYPE;
NEWRECORD myflashtable%ROWTYPE;
dynamic_statement varchar2(4000);
cursor colnames is select * from all_tab_columns where table_name = 'myflashtable';
begin
if not myflash%ISOPEN then
open myflash;
end if;
fetch myflash into NEWRECORD;
while myflash%FOUND loop;
for columnnames in colnames loop
/* cobble together dynamic SQL along the lines of
"if oldrecord.column_name != newrecord.column_name
then print some information``....end if;"
*/
execute immediate dynamic_statement;
end loop;
OLDRECORD := NEWRECORD;
fetch myflash into NEWRECORD;
end loop;
end;
Naturally this didn't work. Initially it gave me "invalid SQL statement" and I added begin/end onto the dynamic SQL. When I tried running that version, it gave me an error because it doesn't know about the old/new records. When I run without doing the execute, but just dumping the generated SQL, it is stepping through all the columns on each of the records, so that part of the logic is working.
I'm quite sure there's a better way to do this, or perhaps to make it work. One thought was to do something like declaring old/new value variables, then using dynamic SQL to move the old/new record fields to each of those:
EXECUTE IMMEDIATE 'oldvalue := OLDRECORD.'||columnnames.column_name;
EXECUTE IMMEDIATE 'newvalue := NEWRECORD.'||columnnames.column_name;
IF oldvalue != newvalue then
/* print some stuff */
END IF:
but of course the trick is that the target variable would have to handle columns of a bunch of different types - char, date, etc. So there'd need to be variants of old/newvalue variables, and logic to handle that, and it was turning into not-so-much-fun.
Any suggestions for a more elegant way to do this? I've checked around the site and haven't had much like finding anything that quite seemed like what I'm trying to do.
You are on the right track. But it is quite some more programming work to do. Read the old and new table in a join linking it with the correct primary key and loop through it. You can use DMBS_SQL package to build a dynamic cursor and loop through the tables.

Resources