Access other values ​in a trigger before save Oracle - oracle

Is it possible to access the previous values ​​that have not yet been stored in the database?
I have a table related to a particular module (MOD) which I will call table XA.
I can insert multiple records into XA simultaneously they are going to be inserted, I cannot change this fact.
For example, the following data is inserted in XA
ID | ParentId | Type | Name | Value
1 | 1 | 5 | Cost | 20000
2 | 1 | 9 | Risk | 10000
And I need in this case to insert / update a record in this same table. A calculated value
At the moment of executing the trigger, the value with the name of Cost for example is inserted first, and then the value of Risk.
When evaluating the Risk, I must have the ability to know what the Cost value is to make the calculation and insert the calculated record.
I tried to create a Package to which I would feed the data, but I still have the same problem.
create or replace PACKAGE GLOBAL
IS
PRAGMA SERIALLY_REUSABLE;
TYPE arr IS TABLE OF VARCHAR2 (32)
INDEX BY VARCHAR2 (50);
NUMB arr;
END GLOBAL;
//Using in trigger
GLOBAL.NUMB (:NEW.ID || '-' || :NEW.ParentId) := :NEW.Value;
BEGIN
IF :NEW.Type == 9 AND GLOBAL.NUMB (5 || '-' || :NEW.ParentId) IS NOT NULL
THEN
// calculate and insert record
ELSE IF :NEW.Type == 5 AND GLOBAL.NUMB (9 || '-' || :NEW.ParentId) IS NOT NULL
// calculate and insert record
END IF;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
// NOT HAVE TWO INSERT TO SAME REGISTER
END;
Values ​​5 and 9 are for reference.
Both records are not always inserted, one or more can be inserted, even the calculated value can be imputed but must be replaced by the calculation.
And I can't create a view since there is an internal process that depends on this particular table.

Do you really really must store calculated value into a table? That's usually not the best idea as you have to maintain it in any possible case (inserts, updates, deletes).
Therefore, another suggestion: a view. Here's an example; my "calculation" is simple, I'm just subtracting cost - risk as I don't know what you really do. If calculation is very complex and should be run every time on a very large data set, yes - performance might suffer.
Anyway, here you go; see if it helps.
Sample data:
SQL> select * From xa order by parentid, name;
ID PARENTID TYPE NAME VALUE
---------- ---------- ---------- ---- ----------
1 1 5 Cost 20000
2 1 9 Risk 10000
5 4 5 Cost 4000
7 4 9 Risk 800
A view:
SQL> create or replace view v_xa as
2 select id,
3 parentid,
4 type,
5 name,
6 value
7 from xa
8 union all
9 select 0 id,
10 parentid,
11 99 type,
12 'Calc' name,
13 sum(case when type = 5 then value
14 when type = 9 then -value
15 end) value
16 from xa
17 group by parentid;
View created.
What does it contain?
SQL> select * from v_xa
2 order by parentid, type;
ID PARENTID TYPE NAME VALUE
---------- ---------- ---------- ---- ----------
1 1 5 Cost 20000
2 1 9 Risk 10000
0 1 99 Calc 10000
5 4 5 Cost 4000
7 4 9 Risk 800
0 4 99 Calc 3200
6 rows selected.
SQL>

Related

Validating decimals in a column - oracle sqldeveloper

Im trying to come up with a test that validates decimals in a particular column (with 220000 records). For example for column A there shouldn't be any values with more decimals than 2, 1 is also ok.
for example :
Column A (datatype varchar)
48528.64
135082.54
5249.1
I tried with round function but than I get an error saying invalid number.
Also I would like to be able to change the number of decimals I put in the test to use with different columns
For example
Its 1 big table with all columns having datatype VARCHAR2(2000 char)
examples for columns:
total amount (value should have no more than 2 decimals)
48528.64
135082.54
349.1123 (not OK)
Balance (value should have no more than 2 decimals)
45428.64
1895082.11
5249.1483 (not OK)
Loan (value should have no more than 6 decimals)
100.64
88999.11654
1000.178875554 (not OK)
For each column I want to set up a seperate test that checks if the value is within the number of decimals allowed. So preferable a select statement with a where clause where I can adjust the numbers of decimals so I end up with all records having 1 or 2 decimals, or all the records that have more than 2 decimals
Invalid number error is due to the fact that you have something that isn't a number in that column, so when you apply numeric function to it, Oracle complains. That's what you get when you store numbers as strings. Don't do that.
Anyway, here's one option which shows what you might try to do: as these are strings, calculate number of digits right of the decimal point.
SQL> select * From test;
A
--------------------
48528.64 -- OK
135082.54 -- OK
5249.1 -- OK
1.2345 -- not OK
-25.553 -- not OK
SQL> select *
2 from test
3 where length(regexp_substr(a, '\d+$')) > 2;
A
--------------------
1.2345
-25.553
SQL>
If there are several columns and you'd like to check each of them using a separate table which holds allowed number of decimals, then you could do something like this:
SQL> with
2 big (total, balance, loan) as
3 (select 48528.64 , 45428.64 , 100.64 from dual union all
4 select 135082.54 , 1895082.11 , 88999.11654 from dual union all
5 select 349.1123 , 5249.1483, 1000.178875554 from dual
6 ),
7 septest (tdec, bdec, ldec) as
8 (select 2, 2, 6 from dual)
9 select
10 b.total,
11 case when length(regexp_substr(b.total,'\d+$')) > s.tdec then 'Not OK'
12 else 'OK'
13 end total_ok,
14 --
15 b.balance,
16 case when length(regexp_substr(b.balance,'\d+$')) > s.bdec then 'Not OK'
17 else 'OK'
18 end balance_ok,
19 --
20 b.loan,
21 case when length(regexp_substr(b.loan,'\d+$')) > s.ldec then 'Not OK'
22 else 'OK'
23 end loan_ok
24 from big b cross join septest s;
TOTAL TOTAL_OK BALANCE BALANCE_OK LOAN LOAN_OK
---------- ---------- ---------- ---------- ---------- ----------
48528,64 OK 45428,64 OK 100,64 OK
135082,54 OK 1895082,11 OK 88999,1165 OK
349,1123 Not OK 5249,1483 Not OK 1000,17888 Not OK
SQL>
Lines #1 - 8 represent sample data; you already have that. Query you actually need begins at line #9.

How to return rows based on the database user and the table's contents?

I have a following table:
id name score
1 SYS 4
2 RHWTT 5
3 LEO 4
4 MOD3_ADMIN 5
5 VPD674 4
6 SCOTT 5
7 HR 4
8 OE 5
9 PM 4
10 IX 5
11 SH 4
12 BI 5
13 IXSNEAKY 4
14 DVF 5
I want to create a policy function in Oracle SQL that makes sure of the following things:
If a user(Leo) is executing a select statement on this table, it only gets 3 LEO 4.
sys_dba gets all the results no matter what.
I have given select permissions to Leo on this table created by Scott.
I am getting stuck at writing this complex PL/SQL function. I tried the following and it states compilation errors. Also, I think it does not do what I intend to do:
CREATE FUNCTION no_show_all (
p_schema IN NUMBER(5),
p_object IN VARCHAR2
)
RETURN
AS
BEGIN
RETURN 'select avg(score) from scott.rating';
END;
/
Based on your previous question and info you posted, here's how I understood the question: if you granted select on the whole table to any user, then it is able to fetch all rows from it. You have to further restrict values.
One option - as we're talking about the function - is to use case in where clause.
Here's an example.
Sample data:
SQL> create table rating as
2 select 1 id, 'sys' name, 4 score from dual union all
3 select 3, 'leo' , 3 from dual union all
4 select 6, 'scott' , 5 from dual union all
5 select 7, 'hr' , 2 from dual;
Table created.
Function:
it accepts username as a parameter (mind letter case! In my example, everything is lowercase. In your, perhaps you'll have to use upper function or something like that)
case says: if par_user is equal to sys, let it fetch all rows. Otherwise, fetch only rows whose name column's value is equal to par_user
return the result
So:
SQL> create or replace function f_rating (par_user in varchar2)
2 return number
3 is
4 retval number;
5 begin
6 select avg(score)
7 into retval
8 from rating
9 where name = case when par_user = 'sys' then name
10 else par_user
11 end;
12 return retval;
13 end;
14 /
Function created.
Let's try it:
SQL> select f_rating('sys') rating_sys,
2 f_rating('hr') rating_hr
3 from dual;
RATING_SYS RATING_HR
---------- ----------
3,5 2
SQL>
I suggest creating a view for each user, like so
create view THE_VIEW as select * from TABLE where NAME = user
Then grant access to the view only.
Now it doesn't matter what kind of query a user tries to perform on your table, she will only get one row back.
Of-course the DBA user can access all the table data.

Oracle:To realize the column automatic management

dataSource:
username type rank
a 106 1
a 116 2
a 126 3
b 106 1
b 106 2
when remove a,116,2 this record return:
username type rank
a 106 1
a 126 2
b 106 1
b 106 2
when insert a,116 return:
username type rank
a 106 1
a 126 2
a 116 3
b 106 1
b 106 2
I choose use tigger to realize:
insert(succeed):
create or replace trigger bi_auto
before insert
on auto
for each row
declare
-- local variables here
begin
select count(rank)+1 into :new.rank from auto where username=:new.username;
end bi_auto;
delete(fail,return ora-04091, ora-06512, ora-04088):
create or replace trigger bd_auto
after delete
on auto
for each row
declare
-- local variables here
begin
insert into session_auto
select username, type, rank() over(partition by username order by rank) ranknew from auto where username=:old.username order by username;
delete from auto where username=:old.username;
insert into auto select * from session_auto;
end bd_auto;
Please help me to modify it,thanks.I know there is something wrong with the performance, but I want to know how to realize.
I think the entire approach is problematic and error prone. It would be much easier to calculate the rank dynamically. If you want the convenience of querying a table, you could just add it to a view:
CREATE OR REPLACE VIEW auto_view AS
SELECT username, type, RANK() OVER (PARTITION BY username ORDER BY TYPE ASC) r
FROM auto
If performance is that big of an issue, you could always materialize your view.

What if the value of order field is the same for all the records [duplicate]

This question already has answers here:
Why does Oracle return specific sequence if 'orderby' values are identical?
(4 answers)
Closed 7 years ago.
All, Let's say the SQL looks like below.
Select a, b ,c from table1 order by c
If all the rows in table1 have the same field value in the field c. I want to know if the result has the same order for each time I executed the SQL.
Let's say data in the table1 looks like below.
a b c
-------------------------------------------
1 x1 2014-4-1
....
100 x100 2014-4-1
....
1000 x1000 2014-4-1
....
How Oracle determine the rows sequence for the same order by value?
Added
Will they be random sequence for each time?
One simple answer is NO. There is no guarantee that the ORDER BY on equal values will return the same sorted result every time. It might seem to you it is always stable, however, there are many reasons when it could change.
For example, the sorting on equal values might defer after:
Gathering statistics
Adding an index on the column
For example,
Let's say I have a table t:
SQL> SELECT * FROM t ORDER BY b;
A B
---------- ----------
1 1
2 1
3 2
4 2
5 3
6 3
6 rows selected.
The sorting on the column having similar values is just like:
SQL> CREATE TABLE t1 AS SELECT * FROM t ORDER BY b, DBMS_RANDOM.VALUE;
Table created.
SQL> SELECT * FROM t1 ORDER BY b;
A B
---------- ----------
1 1
2 1
4 2
3 2
5 3
6 3
6 rows selected.
So, similar data in bot the tables, however, ORDER BY on the column having equal values, dos not guarantee the same sorting.
They must not be random (change each time), but the order is not guaranteed (change sometimes).

One Select, Two Resultsets: Returning Summary and Detail based on same query

I'm attempting to perform a large query where I want to:
Query the detail rows, then
Perform aggregations based on the results returned
Essentially, I want to perform my data-intensive query ONCE, and derive both summary and detail values from the one query, as the query is pretty intensive. I'm SURE there is a better way to do this using the frontend application (e.g. detail rows in the SQL, aggregate in front-end?), but I want to know how to do this all in PL/SQL using essentially one select against the db (for performance reasons, I don't want to call essentially the same large Select twice)(and at this point, my reasons for wanting to do it in one query might be called stubborn... i.e. even if there's a better way, I'd like to know if it can be done).
I know how to get the basic "detail-level" resultset. That query would return data such as:
UPC-Region-ProjectType-TotalAssignments-IncompleteAssignments
So say I have 10 records:
10-A-X-20-10
11-B-X-10-5
12-C-Y-30-15
13-C-Z-20-10
14-A-Y-10-5
15-B-X-30-15
16-C-Z-20-10
17-B-Y-10-5
18-C-Z-30-15
19-A-X-20-10
20-B-X-10-5
I want to be able to perform the query, then perform aggregations on that resultset, such as:
Region A Projects: 3
Region A Total Assign: 50
Region A Incompl Assign: 25
Region B...
Region C...
Project Type X Projects: 5
Project Type X Total Assign: 90
Project Type X Incompl Assign: 45
Project Type Y...
Project Type Z...
And then return both resultsets (Summary + Detail) to the calling application.
I guess the idea would be running the Details query into a Temp Table, and then selecting/performing aggregation on it there to build the second "summary level" query. then passing the two resultsets back as two refcursors.
But I'm open to ideas...
My initial attempts have been:
type rec_projects is record
(record matching my DetailsSQL)
/* record variable */
project_resultset rec_projects;
/* cursor variable */
OPEN cursorvar1 FOR
select
upc,
region,
project_type,
tot_assigns,
incompl_assigns
...
Then I:
loop
fetch cursorvar1 into project_resultset;
exit when cursorvar1%NOTFOUND;
/* perform row-by-row aggregations into variables */
If project_resultset.region = 'A'
then
numAProj := numAProj + 1;
numATotalAssign := numATotalAssign + project_resultset.Totassigns;
numAIncomplAssign := numAIncomplAssign + project_resultset.Incomplassigns;
and so on...
end loop;
Followed by opening another refcursor var - selecting the variables from DUAL:
open cursorvar2 for
select
numAProj, numATotalAssign, numAIncomplAssign, etc, etc from dual;
Lastly:
cur_out1 := cursorvar1;
cur_out2 := cursorvar2;
not working... cursorvar1 seems to load fine, and I get into the loop. But I'm not ending up with anything in cursorvar2, and just feel I'm probably totally on the wrong path here (that there is a better way to do it)
Thanks for your help.
I prefer doing all calculations on server side.
Both types of information (detail + master) can be fetched through single cursor:
with
DET as (
-- your details subquery here
select
UPC,
Region,
Project_Type,
Total_Assignments,
Incomplete_Assignments
from ...
)
select
UPC,
Region,
Project_Type,
Total_Assignments,
Incomplete_Assignments,
null as projects_ctr
from DET
union all
select
null as UPC,
Region,
null as Project_Type,
sum(Total_Assignments) as Total_Assignments,
sum(Incomplete_Assignments) as Incomplete_Assignments,
count(0) as projects_ctr
from DET
group by Region
union all
select
null as UPC,
null as Region,
Project_Type,
sum(Total_Assignments) as Total_Assignments,
sum(Incomplete_Assignments) as Incomplete_Assignments,
count(0) as projects_ctr
from DET
group by Project_Type
order by UPC nulls first, Region, Project_Type
Result:
UPC Region Project_Type Total_Assignments Incomplete_Assignments Projects_Ctr
------ ------ ------------ ----------------- ---------------------- ------------
(null) A (null) 50 25 3
(null) B (null) 60 30 4
(null) C (null) 100 50 4
(null) (null) X 90 45 5
(null) (null) Y 50 25 3
(null) (null) Z 70 35 3
10 A X 20 10 (null)
11 B X 10 5 (null)
12 C Y 30 15 (null)
13 C Z 20 10 (null)
14 A Y 10 5 (null)
15 B X 30 15 (null)
16 C Z 20 10 (null)
17 B Y 10 5 (null)
18 C Z 30 15 (null)
19 A X 20 10 (null)
20 B X 10 5 (null)
fiddle
If you are going to be creating these reports regularly, it might be better to create a global temporary table to store the results of your initial query:
CREATE GLOBAL TEMPORARY TABLE MY_TEMP_TABLE
ON COMMIT DELETE ROWS
AS
SELECT
UPC,
Region,
ProjectType,
TotalAssignments,
IncompleteAssignments
FROM WHEREVER
;
You can then run a series of follow-up queries to calculate the various statistics values for your report and output them in a format other than a large text table.

Resources