Execute Multiple Queries in Oracle Apex Pl/Sql - oracle

i have button with dynamic action execute pl/sql Code
on button click i have to execute two queries, initially insert in a table
and then update in another table
INSERT INTO student (student_name,
student_father,
student_dob,
student_gender,
country,
email_id,
whatsapp_number,
good_time_to_contact,
time_requested,
mobile_number,
state_province,
city,
zip_code,
skype_id,
street_adress,
course,
language_required,
class_days,
application_id,
updated_by)
SELECT first_name || ' ' || last_name AS StudentName,
father_name,
date_of_birth,
gender,
country_id,
email,
whatsapp_number,
time_to_contact,
learning_time,
mobile_number,
state_province,
city,
zip_code,
skype_id,
street_address,
course_id,
language,
class_days,
:P164_APP_ID,
:App_user
FROM student_app
WHERE app_id = :P164_APP_ID;
UPDATE student_app
SET gr_number =
(SELECT gr_number
FROM student
WHERE application_id = :P164_APP_ID),
updated_by = :App_user,
app_status = '6-STUDYING',
updated_ts = CURRENT_TIMESTAMP
WHERE app_id = :P164_APP_ID;
i am using these pl/sql code but does insert in the table nor update either.
while these code execute fine when i execute from oracle sql developer
please help me out i am using oracle apex 18.2

It seems that you didn't commit, did you?
Also, check whether you put page item(s) you use (P164_APP_ID) into the Items to submit dynamic action property (it is right below the PL/SQL code).

If your items do contain the values in the session (that you have to submit to the page, as Littlefoot said) and it still doesn't work, try adding your query to a Process in the Processing tab, with the server-side condition set to pressing the button you're pressing. I can't remember if you can set multiple statements to be run in a single process, but it's worth a try.
Something like:
Processing Tab
Code Section
Condition Section

Related

how to track and modify an in-flight oracle query before execution?

My Application is sending below query to Oracle.
SELECT * FROM EMPLOYEE WHERE DATE > (SYSDATE - 1) order by employee_id
I cannot change this query from the application. I am looking for a way for oracle to monitor the queries, change it to below query format and return the result.
SELECT * FROM EMPLOYEE WHERE DATE > (SYSDATE - 1) and Currently_employed = 'YES' order by employee_id
Thank You.
Subrat
If the application can connect to the database as a different user then the table owner, you could also implement a Virtual Private Database policy to modify the query in-flight. See here:
https://oracle-base.com/articles/8i/virtual-private-databases
You could also, if the application is connecting as a separate user, create a view as suggested by #ekochergin, and have a synonym "EMPLOYEE" in the application's user schema point to the view in the data owner's schema.
If the application is connecting as the data owner, your options are much more limited. Renaming the table and replacing it with a view as suggested would be the easiest option. If you've got a lot of money to invest in Oracle's Database Application Firewall appliance you could also modify the query in-flight with a security policy there.
You might needed to rename employee table to somewhat like "EMP_TABLE" and create a view named "EMPLOYEE" using
create view employee as select * from emp_table where currently_employed = 'YES';
Please test it against a test instance before implementing on live
Use the SQL Translation Framework if you only need to convert a small number of statements. If you need to modify many statements then you should look into the options described in the other answers, such as Virtual Private Database, views, or synonyms.
For this sample schema:
create table employee
(
employee_id number,
hire_date date,
currently_employed varchar2(3)
);
insert into employee values(1, sysdate, 'NO');
insert into employee values(1, sysdate, 'YES');
commit;
Create the following translator profile and then create the specific translation:
begin
dbms_sql_translator.create_profile('EMPLOYEE_TRANSLATOR_PROFILE');
dbms_sql_translator.register_sql_translation
(
profile_name => 'EMPLOYEE_TRANSLATOR_PROFILE',
sql_text => q'[SELECT * FROM EMPLOYEE WHERE HIRE_DATE > (SYSDATE - 1) order by employee_id]',
translated_text => q'[SELECT * FROM EMPLOYEE WHERE HIRE_DATE > (SYSDATE - 1) and Currently_employed = 'YES' order by employee_id]'
);
end;
/
The translation profile must be enabled in each session. Since you have no control over the application, you can create a logon profile that will automatically run the commands to enable the translation:
--Logon trigger that enables profiles.
--I'm not sure why, but you must create this trigger as SYS.
create or replace trigger sys.logon_trg
after logon on database
--Add your username here:
when (user in ('JHELLER'))
begin
execute immediate 'alter session set sql_translation_profile = jheller.employee_translator_profile';
execute immediate q'[alter session set events = '10601 trace name context forever, level 32']';
end;
/
Now, when the application runs the original query that would normally return two rows, it will run the second query that only returns one row:
SQL> SELECT * FROM EMPLOYEE WHERE HIRE_DATE > (SYSDATE - 1) order by employee_id;
EMPLOYEE_ID HIRE_DATE CUR
----------- --------- ---
1 12-FEB-21 YES
But be careful of tiny syntax changes that will prevent the translation. For example, if SELECT is changed to select, the query will not be replaced and will return two rows again:
SQL> select * FROM EMPLOYEE WHERE HIRE_DATE > (SYSDATE - 1) order by employee_id;
EMPLOYEE_ID HIRE_DATE CUR
----------- --------- ---
1 12-FEB-21 YES
1 12-FEB-21 NO

Oracle DB. Insert Trigger with Merge statament inside. Table is mutating

I have two back-end systems (the old one and the new one) that shares an Oracle DB.
In the older system, to save customers data, there are two tables
customers_A
ID NAME ETC
1 PETE ....
customers_B
ID NAME ETC
1 JOSH ...
2 ROSS ...
In the new system I've created a new table called All_Costumer, to join those tables.
This new table contains customer ID's of type A and B respectively.
All_Customers
ID ID_CUSTOMER_A ID_CUSTOMER_B
A19E----D2B0 1 null
A19E----D2B1 null 1
A19E----D2B2 null 2
So, when the new system creates a new customer of type A, data are inserted on customer_A and All_Customers tables, with customer of type B as well.
Currently, the old system is working too, and when a new customer of type A is created, data is inserted only on customer_A table, but I need that data in All_Customers too.
To solve this, I've created a TRIGGER with a MERGE INTO statement inside, to insert a row in All_Customers if doesn't exist on this table (when a new customer of type A are created by the older system)
CREATE OR REPLACE TRIGGER customers_trg
AFTER INSERT
ON customer_A
FOR EACH ROW
DECLARE
variables that doesn't matters
BEGIN
MERGE INTO all_customers
USING (SELECT :new.id id FROM customer_A where id = :new.id) customer
ON (all_customers.id_customer_a = customer.id)
WHEN NOT MATCHED THEN
INSERT (id, id_customer_a)
VALUES (SYS_GUID(), :new.id, null);
COMMIT;
END;
But when I try to create a new customer from the older system, I get this error:
ORA-04091: table **customer_A** is mutating, trigger/function may not see it
Any idea to solve this?
I've tried adding PRAGMA AUTONOMOUS_TRANSACTION; on DECLARE section, but didn't work.
Note: I can't modify the old system
The immediate issue is that you're querying table_a in a trigger against that table; but you don't need to. Your merge query
SELECT :new.id id FROM customer_A where id = :new.id
can simply do
SELECT :new.id id FROM dual
i.e. the clause becomes:
...
USING (SELECT :new.id id FROM dual) customer
ON (all_customers.id_customer_a = customer.id)
...
You also can't commit in a trigger - unless it's autonomous, which this shouldn't be. You said you'd tried that, but it breaks if the insert is rolled back, since the merged row will still exist. So hopefully that commit is just a hang-over from trying and rejecting that approach.
But it works in this db<>fiddle, anyway.
If you weren't adding the GUID you could get the same effect with a view:
create or replace view all_customers (id_customer_a, id_customer_b) as
select id, null from customers_a
union all
select null, id from customers_b;
db<>fiddle

oracle database data synchronized from one to multi relative tables

I hava a user table , which is quite simple
create table user (
user_id int primary key,
user_name varchar2(20)
)
And I build a couples of relative tables assocaite with user table and each table has a user_id , user_name.
So here comes a question, I happend misinput a data with wrong name, but then I just linked to this wrong record with all relative tables. If I want correct the user table and same time synchronized user_name in all relative tables.How I do in simple way? Plus I didn't set any constraint with these tables.
Edit:
So let me put that more clearly. I can query all user from user table, and then I just create a select in the jsp page. And this selector got two field user_id, user_name. This is how we call it a selector. First I recorded a man with '01','tam' maybe, and I just recorded another row in salary with 'tam','$1300'. This was all wrong cause name was 'tom'. It's easily to change user or salary , but in our system, there are over 40 tables linked to user. I know it's a bad idea but it is designed that way
by our dba and it already worked a long time.
We'll start by making the problem explicit. The data model violates Third Normal Form: instead of relying on user_id to reference user_name every table dependent on the user table has the attribute. The consequence of this is that correcting a mistake in user_name means propagating that change to every table.
Further more it seems that this application lacks a mechanism for correcting errors, or rather propagating the correction to all the impacted tables. So, what to do?
Dynamic SQL and the data dictionary to the rescue:
declare
l_id user.user_id%type := 1234;
l_old_name user.user_name%type := 'Tam';
l_new_name user.user_name%type := 'Tom';
begin
for rec in ( select table_name from user_tab_cols where column_name = 'USER_ID'
intersect
select table_name from user_tab_cols where column_name = 'USER_NAME'
)
loop
execute immediate 'update '|| rec.table_name ||
' set user_name = :1 where user_id = :2 and user_name = :3'
using l_new_name, l_id, l_old_name;
commit;
end loop;
end;
/
No guarantees about performance, because it depends on the data and indexing for each table.
"it already worked a long time"
Which makes me wonder how many data inconsistencies are contained in your system that you don't know about? Maybe your DBA needs to brush up on their data modelling skills.

Print column2 from row with max(column1) without including column2 in group by clause

I know it is a silly question and may be already answered somewhere, please guide me to the link if it is.
I want to print a column which is not included in group by clause. Oracle says that it should be included in group by expression, but I want value to be from the same row from which max() value for the other column was selected.
For example: if I have a table with following columns:
Employee_Name, Action_code, Action_Name
I want to see the name of action with maximum action_code for each employee, also I cannot use subquery in the condition.
I want some thing like this:
select employee_name, max(action_code), action_name --for max code
from emp_table
group by employee_name
This action_name in select statement is causing problem, if I add action_name in group by clause then it will show action name for each action for each employee, which will make the query meaningless.
Thanks for support
You can use a keep .. last pattern:
select employee_name,
max(action_code) as action_code,
max(action_name) keep (dense_rank last order by action_code) as action_name
from emp_table
group by employee_name
The documentation explains this more fully under the sister function first().

Getting values from a TABLE using SELECT inside a TRIGGER (Oracle SQL)

I am working on a Music Collection Database Project and I am stuck on this TRIGGER code. There is an Album table and the task of the trigger is to check if the album being entered already exists in the table or not. And if it does, it should copy the rating which is already present in the table for the album irrespective of the new one entered by the user. (I am using Oracle 11g)
The trigger I managed to get is :
CREATE OR REPLACE TRIGGER album_constraints BEFORE INSERT ON Album
DECLARE
a_id int;
a_title varchar(50);
a_duration varchar(6);
a_number_of_tracks int;
a_recorded date;
a_rating int;
BEGIN
SELECT album_id,
album_title,
album_duration,
album_number_of_tracks,
album_recorded,
album_rating
INTO a_id,
a_title,
a_duration,
a_number_of_tracks,
a_recorded,
a_rating
FROM Album
WHERE album_title = new.album_title and
album_duration = new.album_duration and
album_number_of_tracks = new.album_number_of_tracks and
album_recorded = new.album_recorded and
rownum = 1;
IF new.album_title = a_title and
new.album_duration = a_duration and
new.album_number_of_tracks = a_number_of_tracks and
new.album_recorded = a_recorded
THEN
new.album_rating := a_rating;
END IF;
END;
Can someone please help me achieve this? I am getting multiple errors such as "PL-SQL : Statement ignored"
Actually, there is a way to do what you are trying to do using a single table. It just doesn't involve triggers. Here's a code fragment showing the insert statement:
begin
insert into Album( id, title, duration, number_of_tracks, recorded, rating )
values( v_id, v_title, v_duration, v_number_of_tracks, v_recorded, v_rating );
exception
when DUP_VAL_ON_INDEX then
update Album
set rating = v_rating
where id = v_id;
end;
This assumes ID is the PK and it is something like an ISPN number which uniquely identifies each published album, not a sequential value. If the album does not exist in the table, the Insert statement executes normally. If the album already exists, the Insert statement fails, the exception is caught and the Update is executed instead.
If you really, really want to use a trigger, then front the table with a view and create an "instead of" trigger on the view. To avoid having to make any changes in the app code, name the view Album and the table something else. (My convention is to call the table "Album_". The trailing underscore tells me that no direct access to the table is allowed and there is a view without an underscore where all access takes place. You can use whatever convention you want, of course.)
CREATE OR REPLACE TRIGGER album_constraints
instead of INSERT ON Album
for each row
begin
insert into Album_( id, title, duration, number_of_tracks, recorded, rating )
values( :new.id, :new.title, :new.duration, :new.number_of_tracks, :new.recorded, :new.rating );
exception
when DUP_VAL_ON_INDEX then
update Album_
set rating = :new.rating
where id = :new.id;
end;
Here is where I have to give a hat tip to Sql Server. It allows "instead of" triggers on tables making a "pass thru" view unnecessary.

Resources