Suppose we have the following two tables:
OrderHeader(OrderID, Odate, CustID, Total)
Order_Item(OrderID,ItemID, Qty, Subtotal)
Write a statement-level trigger that updates the Total in the orderHeader table with
the total value of the order_item records whenever an insert, update or delete event
occurs on the order_item table. For any update error, raise an exception.
So far I have written this:
create or replace TRIGGER SECURE_ORDER
AFTER INSERT OR DELETE OR UPDATE ON Order_Item
declare
sum1 Number;
BEGIN
SELECT SUM(Qty*Price) Into sum1 FROM Order_Item Inner join Item Using(ItemID) WHERE OrderID=:OLD.OrderID;
UPDATE OrderHeader set Total=sum1 where OrderID=:OLD.OrderID;
EXCEPTION
WHEN too_many_rows then
dbms_output.put_line('Too many rows');
WHEN no_data_found then
dbms_output.put_line('No Data Found');
WHEN others then
dbms_output.put_line('other error');
END;
It is not correct however as the question statement says I have to write a statement level trigger which doesn't give me acces to NEW and OLD keyword. I don't know how to go about doing this in a statement level trigger. Any help would be appreciated.
You can just use a single MERGE statement:
CREATE TRIGGER SECURE_ORDER
AFTER INSERT OR DELETE OR UPDATE ON Order_Item
BEGIN
MERGE INTO OrderHeader dst
USING (
SELECT COALESCE( oh.OrderID, o.OrderID ) AS OrderID,
COALESCE( SUM( o.Qty*i.Price ), 0 ) AS total
FROM OrderHeader oh
FULL OUTER JOIN Order_Item o
ON oh.OrderID = o.OrderID
LEFT OUTER JOIN Item i
ON ( o.ItemID = i.ItemID )
GROUP BY COALESCE( oh.OrderID, o.OrderID )
) src
ON ( src.OrderID = dst.OrderID )
WHEN MATCHED THEN
UPDATE SET total = src.total
WHEN NOT MATCHED THEN
INSERT ( OrderID, Total ) VALUES ( src.OrderID, src.Total );
END;
/
Then, if you have the tables:
CREATE TABLE Item (
ItemID NUMBER(10,0)
GENERATED ALWAYS AS IDENTITY
CONSTRAINT Item__ItemID__PK PRIMARY KEY,
Name VARCHAR2(20)
NOT NULL,
Price NUMBER(12,2)
NOT NULL
CONSTRAINT Item__Price_GTE_0__CHK CHECK ( Price >= 0 )
);
CREATE TABLE Order_Item (
OrderID INT
-- CONSTRAINT Order_Item__OrderID__FK REFERENCES Orders ( OrderID )
NOT NULL,
Qty INT
NOT NULL
CONSTRAINT Order_Item__Qty_GT_0__CHK CHECK ( Qty > 0 ),
ItemID INT
NOT NULL
CONSTRAINT Order_Item__ItemID__FK REFERENCES Item ( ItemId ),
CONSTRAINT Order_Item__OID__IID__PK PRIMARY KEY ( OrderID, ItemID )
);
CREATE TABLE OrderHeader (
OrderID INT
NOT NULL,
total NUMBER(14,2)
NOT NULL
);
Then if you:
INSERT INTO Item ( Name, Price ) VALUES ( 'Item 001', 1.00 );
INSERT INTO Item ( Name, Price ) VALUES ( 'Item 002', 2.00 );
INSERT INTO Item ( Name, Price ) VALUES ( 'Item 003', 2.50 );
INSERT INTO Order_Item ( OrderID, ItemID, Qty ) VALUES ( 1, 1, 3 );
INSERT INTO Order_Item ( OrderID, ItemID, Qty ) VALUES ( 1, 2, 1 );
INSERT INTO Order_Item ( OrderID, ItemID, Qty ) VALUES ( 1, 3, 2 );
The OrderHeader table contains:
SELECT * FROM OrderHeader;
ORDERID | TOTAL
------: | ----:
1 | 10
Then if you do:
UPDATE Order_Item
SET Qty = 8
WHERE OrderID = 1
AND ItemID = 1;
The OrderHeader table contains:
SELECT * FROM OrderHeader;
ORDERID | TOTAL
------: | ----:
1 | 15
Then if you do:
DELETE FROM Order_Item
WHERE ( OrderID, ItemID ) IN ( (1,2), (1,3) );
The OrderHeader table contains:
SELECT * FROM OrderHeader;
ORDERID | TOTAL
------: | ----:
1 | 8
And, finally, if you do:
DELETE FROM Order_Item;
The OrderHeader table contains:
SELECT * FROM OrderHeader;
ORDERID | TOTAL
------: | ----:
1 | 0
db<>fiddle here
Related
I have this table: employees(id, firstname, lastname, salary) and I added another column that will store all the past salaries of every employee.
CREATE OR REPLACE TYPE salary_list AS TABLE OF NUMBER;
/
ALTER TABLE employees
ADD (salary_history salary_list)
NESTED TABLE salary_history STORE AS salary_history_tab;
Now, the table is employees(id, firstname, lastname, salary, salary_history) and salary_history is null. When I modify the salaray value, how do I insert the current salary value into the inner table salary_history? I have tried:
INSERT INTO
TABLE(SELECT salary_history FROM employees WHERE id=1)
VALUES(1500);
And I get the following error:
ORA-22908: reference to NULL table value
Yes, the nested table salary_history is null because I just created it. What am I doing wrong? What is the correct way to insert values(append to the existing data) into salary_history identifying the employee by id?
UPDATE:
added nvl(,()) like so:
INSERT INTO
TABLE(SELECT nvl(salary_history,salary_list()) FROM employees WHERE id=1)
VALUES(1000);
The error that I get now is:
ORA-25015: cannot perform DML on this nested table view column
You can use COALESCE( salary_history, salary_list() ) MULTISET UNION ALL salary_list( :your_new_value ) to append the new value to the old list (or create a new list if it does not exist).
Oracle 11g R2 Schema Setup:
CREATE OR REPLACE TYPE salary_list AS TABLE OF NUMBER;
/
CREATE TABLE employees(
id NUMBER,
salary_history salary_list
) NESTED TABLE salary_history STORE AS salary_history_tab
/
INSERT INTO employees VALUES ( 1, NULL )
/
Query 1:
UPDATE employees
SET salary_history = COALESCE( salary_history, salary_list() )
MULTISET UNION ALL salary_list( 500 )
WHERE id = 1
SELECT * FROM employees
Results:
| ID | SALARY_HISTORY |
|----|----------------|
| 1 | 500 |
Query 2:
UPDATE employees
SET salary_history = COALESCE( salary_history, salary_list() )
MULTISET UNION ALL salary_list( 700 )
WHERE id = 1
SELECT * FROM employees
Results:
| ID | SALARY_HISTORY |
|----|----------------|
| 1 | 500,700 |
Query 3:
UPDATE employees
SET salary_history = COALESCE( salary_history, salary_list() )
MULTISET UNION ALL salary_list( 500 )
WHERE id = 1
SELECT * FROM employees
Results:
| ID | SALARY_HISTORY |
|----|----------------|
| 1 | 500,700,500 |
The column is currently null, rather than an empty table; you can create an empty nested table to start with:
UPDATE employees
SET salary_history = NEW salary_list()
WHERE id = 1;
1 row updated.
And your first statement will then work:
INSERT INTO
TABLE(SELECT salary_history FROM employees WHERE id=1)
VALUES(1500);
1 row inserted.
You can see the new value:
SELECT * FROM employees;
ID FIRSTNAME LASTNAME SALARY SALARY_HISTORY
---------- ---------- ---------- ---------- ------------------------------
1 Joe BLoggs 1234 SALARY_LIST(1500)
what im trying to do is to find number of saving account at each branch, and display the number and branch address.
here is code :
create type address as object
(
street varchar2(20),
city varchar2(10),
p_code varchar2(8)
);
create type Branch as object
(
BId varchar2(3),--branch id
brAddress address,
bPhone int
);
create table tb_Branch of Branch
(
bid primary key
);
create type account as object
(
accNum varchar2(4), --pk
accType varchar2(8), -- check(current, saving)
balance number(8,2)
BId_ref ref BId
);
create tb_account of account
(
accNum primary key,
constraint accType_const check(accType IN ('current','savings'))
);`
Please help!
thanks!
Maybe the following example (Oracle 12c) will help you to find a solution ... (the TYPEs and TABLEs may need tweaking).
Types and tables
-- types
create type address_t as object (
street varchar2(20)
, city varchar2(10)
, postcode varchar2(8)
)
/
create type branch_t as object (
brid varchar2(64)
, braddress address_t
, brphone varchar2(24)
)
/
create type account_t as object (
accnum varchar2(4)
, acctype varchar2(8)
, balance number(12,2)
)
/
-- tables
create table branches (
branch branch_t
, constraint branch_pk primary key ( branch.brid )
);
create table accounts (
id number generated always as identity primary key
, account_details account_t
, constraint acc_type_check check(
account_details.acctype in ('current','savings')
)
, branchid references branches( branch.brid )
);
Some INSERTs (table BRANCHES)
insert into branches ( branch )
values (
branch_t( '66-20-67'
, address_t( '1 High St', 'Edinburgh', 'EH1 1AA' )
, '00441155700340' )
) ;
-- trying to insert this again
-- gives us
-- ORA-00001: unique constraint (...BRANCH_PK) violated -> PK constraint working
insert into branches ( branch )
values (
branch_t( '88-45-13'
, address_t( '2 Princes St', 'Edinburgh', 'EH2 2BB' )
, '00441156800900' )
) ;
-- The BRANCHES table contains:
SQL> select * from branches;
BRANCH(BRID, BRADDRESS(STREET, CITY, POSTCODE), BRPHONE)
---------------------------------------------------------------------------------------------------------------------------
BRANCH_T('66-20-67', ADDRESS_T('1 High St', 'Edinburgh', 'EH1 1AA'), '00441155700340')
BRANCH_T('88-45-13', ADDRESS_T('2 Princes St', 'Edinburgh', 'EH2 2BB'), '00441156800900')
Populate the ACCOUNTS table
begin
insert into accounts ( account_details, branchid )
values ( account_t ( '7890', 'savings', 123456.78 ), '66-20-67' ) ;
insert into accounts ( account_details, branchid )
values ( account_t ( '7891', 'savings', 222222.22 ), '66-20-67' ) ;
insert into accounts ( account_details, branchid )
values ( account_t ( '7892', 'savings', 333333.33 ), '88-45-13' ) ;
insert into accounts ( account_details, branchid )
values ( account_t ( '6789', 'current', 234567.89 ), '88-45-13' ) ;
end;
/
-- fails due to CHECK constraint
insert into accounts ( account_details, branchid )
values ( account_t ( '1234', 'standard', 4567.89 ), '66-20-67' ) ;
-- ORA-02290: check constraint (...ACC_TYPE_CHECK) violated
Query
/*
from your question:
" what im trying to do is
to find number of saving account at each branch,
and display the number and branch address "
*/
-- suggested query
select
A.account_details.acctype as acctype
, B.branch.brid as branchid
, B.branch.braddress.city as city
, count( * )
from branches B
join accounts A on B.branch.brid = A.branchid
where A.account_details.acctype = 'savings'
group by A.account_details.acctype
, B.branch.brid
, B.branch.braddress.city ;
ACCTYPE BRANCHID CITY COUNT(*)
-------- ---------- ---------- ----------
savings 88-45-13 Edinburgh 1
savings 66-20-67 Edinburgh 2
I'm trying to delete rows in a table only when there is a corresponding entry with a negative amount. The tricky part is there could be more positive than negative or more negative than positive.
value1 amt
12345 50
12345 50
12345 -50
12345 -50
abcde 40
abcde 40
abcde -40
11111 30
11111 -30
11111 -30
The result should be:
abcde 40
11111 -30
I have to apologize. I realized the posters data set was too simple. Here is a revised answer that I believe works.
Basically, you need to partition into pairs and then delete the pairs having sum() = 0.
create table t ( id varchar2(20), val number );
delete from t;
INSERT INTO t ( id, val ) values ( '12345', 50);
INSERT INTO t ( id, val ) values ( '12345', 50);
INSERT INTO t ( id, val ) values ( '12345', -50);
INSERT INTO t ( id, val ) values ( '12345', -50);
INSERT INTO t ( id, val ) values ( 'abcde', 40);
INSERT INTO t ( id, val ) values ( 'abcde', 40);
INSERT INTO t ( id, val ) values ( 'abcde', 20);
INSERT INTO t ( id, val ) values ( 'abcde', 40);
INSERT INTO t ( id, val ) values ( 'abcde', -40);
INSERT INTO t ( id, val ) values ( '11111', 30);
INSERT INTO t ( id, val ) values ( '11111', -30);
INSERT INTO t ( id, val ) values ( '11111', -30);
INSERT INTO t ( id, val ) values ( 'aaaaa', 10);
INSERT INTO t ( id, val ) values ( 'aaaaa', -30);
COMMIT;
MERGE INTO t
USING (WITH value_partition AS
(SELECT t.*,
ROW_NUMBER () OVER (PARTITION BY t.id, t.val ORDER BY ROWID) rn_in_value
FROM t)
SELECT sp.ROWID row_id,
sp.*,
CASE WHEN SUM (sp.val) OVER (PARTITION BY sp.id, ABS (sp.val), rn_in_value) = 0 THEN 'N' ELSE 'Y' END
keep_row
FROM value_partition sp) u
ON (t.ROWID = u.row_id
AND u.keep_row = 'N')
WHEN MATCHED THEN
UPDATE SET t.val = u.val
DELETE
WHERE u.keep_row = 'N';
SELECT * FROM t;
Ok so here are my tables in the database:
CREATE DATABASE Temp
GO --------------------------
USE Temp
GO --------------------------
CREATE TABLE Table1
(
Table1Id INT IDENTITY(1, 1) ,
Name VARCHAR(20) ,
CONSTRAINT pk_Table1 PRIMARY KEY ( Table1Id )
)
GO --------------------------
CREATE TABLE Table2
(
Table2Id INT IDENTITY(1, 1) ,
Table1Id INT ,
NAME VARCHAR(20) ,
TheDate SMALLDATETIME ,
CONSTRAINT pk_Table2 PRIMARY KEY ( Table2Id ) ,
CONSTRAINT fk_Table2_Table1 FOREIGN KEY ( Table1Id ) REFERENCES Table1 ( Table1Id )
)
GO --------------------------
INSERT INTO Table1
( Name )
VALUES ( 'Stack Overflow' )
GO --------------------------
INSERT INTO Table1
( Name )
VALUES ( 'Expert Sex Change' )
GO --------------------------
INSERT INTO Table1
( Name )
VALUES ( 'Code Project' )
GO --------------------------
INSERT INTO dbo.Table2
( Table1Id ,
NAME ,
TheDate
)
VALUES ( 1 ,
'S1' ,
'11-01-2012'
)
GO --------------------------
INSERT INTO dbo.Table2
( Table1Id ,
NAME ,
TheDate
)
VALUES ( 1 ,
'S2' ,
'11-01-2013'
)
GO --------------------------
INSERT INTO dbo.Table2
( Table1Id ,
NAME ,
TheDate
)
VALUES ( 2 ,
'E1' ,
'10-01-2013'
)
And here's my LINQ:
from t1 in Table1s
join t2 in Table2s.OrderByDescending(x => x.TheDate)
on t1.Table1Id equals t2.Table1Id into tt
from t2 in tt.DefaultIfEmpty()
select new
{
t1.Table1Id,
t1.Name,
t2.NAME,
t2.TheDate
}
This one returns:
Table1Id - Name - NAME - TheDate
1 - Stack Overflow - S2 - 11/1/2013
2 - Expert Sex Change - E1 - 10/1/2013
1 - Stack Overflow - S1 - 11/1/2012
3 - Code Project - null - null
I want the LINQ query not to return the third line, as is from an older Date Value.
I think I got it, the answer is:
from t1 in Table1s
join t2 in Table2s
on t1.Table1Id equals t2.Table1Id
into tt
from x in tt.DefaultIfEmpty()
// where ... t1 && x ..
orderby t1.Table1Id
group x by new {t1.Table1Id,t1.Name} into g
select new {
Table1Id = g.Key.Table1Id,
Name = g.Key.Name,
TheDate = g.Max(c => c.TheDate)
}
I have this table below in the picture that i want to create as an object-oriented table. I dont want the usual create table with relationships.... I just want to learn how to turn this table into an object-oriented table. Below is a picture of my tables and how are they connected:
CREATE TYPE A_TYPE AS OBJECT(
id INT,
col1 INT
);
/
CREATE TYPE A_REF_TABLE_TYPE AS TABLE OF REF A_TYPE;
/
CREATE TYPE B_TYPE AS OBJECT(
id INT,
col1 INT
);
/
CREATE TYPE B_REF_TABLE_TYPE AS TABLE OF REF B_TYPE;
/
CREATE TYPE C_TYPE AS OBJECT(
id INT,
a_list A_REF_TABLE_TYPE,
b_list B_REF_TABLE_TYPE,
col1 INT
);
/
CREATE TABLE A_TAB OF A_TYPE(
ID PRIMARY KEY
);
CREATE TABLE B_TAB OF B_TYPE(
ID PRIMARY KEY
);
CREATE TABLE C_TAB OF C_TYPE(
ID PRIMARY KEY
)
NESTED TABLE a_list STORE AS c_a_lists
NESTED TABLE b_list STORE AS c_b_lists;
INSERT INTO A_TAB VALUES( A_TYPE( 1, 3 ) );
INSERT INTO A_TAB VALUES( 2, 4 );
INSERT INTO B_TAB VALUES ( B_TYPE( 1, 7 ) );
INSERT INTO B_TAB VALUES ( 2, 2 );
INSERT INTO B_TAB VALUES ( 3, 10 );
INSERT INTO C_TAB VALUES (
1,
A_REF_TABLE_TYPE(
( SELECT REF(a) FROM A_TAB a WHERE ID = 2 ) -- Single value
),
( -- Multiple values
SELECT CAST( COLLECT( REF(b) ) AS B_REF_TABLE_TYPE )
FROM TAB_B b
WHERE ID IN ( 1, 3 )
),
42
);
INSERT INTO C_TAB VALUES (
2,
NULL, -- Unknown
B_REF_TABLE_TYPE(), -- No values
54
);
Output:
SELECT * FROM C_TAB;
ID A_LIST B_LIST COL1
-- ------------------------------- --------------------------------------------- ----
1 A_REF_TABLE_TYPE( A_TYPE(2,4) ) B_REF_TABLE_TYPE( B_TYPE(1,7), B_TYPE(3,10) ) 42
2 (null) B_REF_TABLE_TYPE() 54