Deleting specific record from nested table Oracle DB - oracle

I'm having problems deleting specific record from the table (ORACLE DB).
I have a table with a nested table inside of it.
Table structure looks like this: where ML - nested table
Name, City, ML(Brand, Model, ID, Year, Price)
What I need to do is delete specific record with ID of 'L201'.
What I have tried so far:
SELECT B.ID FROM TABLE Dock A, Table(A.ML) B;
This is working giving me all the ID's.
Output:
ID
____
B201
S196
L201
This is not working when trying to delete the record:
DELETE FROM Dock
(SELECT B.ID FROM Dock A, Table(A.ML) B) C
WHERE C.ID = 'L201';
Getting error:
Line 2: SQL command not properly ended;
DELETE FROM TABLE
(SELECT D.ML FROM Dock D) E
WHERE E.ID = 'L201';
Throws an error:
single-row subquery returns more than one row

Maybe this one:
DELETE FROM
(SELECT A.Name, A.City, d.Brand, d.Model, d.ID, d.Year, d.Price
FROM Dock A, TABLE(ML) d)
WHERE ID = 'L201';
Update:
Another trial before we gonna make it more advanced:
DELETE FROM Dock
WHERE ROWID =ANY (SELECT a.ROWID FROM Dock a, TABLE(ML) b WHERE b.ID = 'L201');
Update 2:
If you prefer it more object-oriented, this one should work as well. At least I did not get any error.
CREATE OR REPLACE TYPE ML_TYPE AS OBJECT (
brand VARCHAR2(100),
ID VARCHAR2(20),
MODEL VARCHAR2(20),
YEAR NUMBER,
Price NUMBER,
MAP MEMBER FUNCTION getID RETURN VARCHAR2,
CONSTRUCTOR FUNCTION ML_TYPE(ID IN VARCHAR2) RETURN SELF AS RESULT);
CREATE OR REPLACE TYPE BODY ML_TYPE IS
CONSTRUCTOR FUNCTION ML_TYPE(ID IN VARCHAR2) RETURN SELF AS RESULT IS
-- Constructor to create dummy ML-Object which contains just an ID,
-- used for comparison
BEGIN
SELF.ID := ID;
RETURN;
END ML_TYPE;
MAP MEMBER FUNCTION getID RETURN VARCHAR2 IS
BEGIN
RETURN SELF.ID;
END getID;
END;
/
CREATE OR REPLACE TYPE ML_TABLE_TYPE IS TABLE OF ML_TYPE;
CREATE TABLE Dock (Name VARCHAR2(20), City VARCHAR2(20), ML ML_TABLE_TYPE)
NESTED TABLE ML STORE AS ML_NT;
insert into Dock values ('A', 'NY', ML_TABLE_TYPE(
ML_TYPE('brand1','L301','Model 2',2013, 1000),
ML_TYPE('brand2','L101','Model 3',2013, 1000)));
insert into Dock values ('B', 'NY', ML_TABLE_TYPE(
ML_TYPE('brand3','K301','Model 4',2014, 3000),
ML_TYPE('brand4','K101','Model 5',2014, 3000)));
insert into Dock values ('A', 'NY', ML_TABLE_TYPE(
ML_TYPE('brand5','K301','Model 8',2012, 2000),
ML_TYPE('brand6','L201','Model 9',2012, 2000)));
DELETE FROM Dock WHERE ML_TYPE('L201') MEMBER OF ML;

After reading some literature I think I found the correct way to do this.
By defining a mapping-function for your object-type you can compare two nested tables directly.
Example:
-- creating the custom object type
create or replace type ml_type as object (
brand varchar2(100),
id varchar2(20),
map member function sort_key return varchar2
);
-- creating the object type body and defining the map-function
create or replace type body ml_type as
map member function sort_key return varchar2 is
begin
return self.brand || '|' || self.id;
end;
end;
/
-- creating the nested table of custom type
create or replace type ml_tab as table of ml_type;
-- deleting from your table by comparing the nested-table elements
delete from dock where ml = (select ml from dock a, table(a.ml) b where b.id = 'L201');
In this example the map-functions is just returning the concatenated version of brand and id, but you can define it to what you want/need.

Related

PLS-00049 : Eroor when using :NEW

I have two tables
A(#ref_medic,libelle, vignette,remarque, qtestock)
B(#ref_medic, #dateF, qte_lot, unite, remarque)
I want to use the instead of insert trigger in order to insert a row in B in case the row having its ref_medic is existent in A but not in B.
I keep getting the error :
PLS-00049: bad bind variable 'NEW.REF_MEDIC'
whenever I'm inserting in this view :
This is the view I'm inserting through thanks to INSTEAD OF INSERT TRIGGER
CREATE VIEW myview(reference,libelle,vignette, date_peremption, Quantite,unite,
Remarque) AS
SELECT M.ref_medic, libelle, vignette, dateF,Qte_lot,unite,LM.remarque
FROM Medicament M, Lot_medicament LM
WHERE M.ref_medic=LM.ref_medic
AND qte_lot>0;
I tried removing :NEW but that's not what i'm looking forward to do as I want to insert this row in B :
INSERT INTO myview VALUES
('12AS45','test','yes','06/06/2021',30,'boite','test');
PLS-00049: variable attachée (bind variable) erronée 'NEW.REF_MEDIC'
We should get the folllowing result: the row won't be inserted in B as it's existant there
Is there a '#' in the name of your column ? it might cause problem.
Otherwise it should be working.
Here is a tested example :
create table Medicament(
ref_medic VARCHAR2(10),
libelle VARCHAR2(30),
vignette VARCHAR2(3),
remarque VARCHAR2(50),
qtestock NUMBER);
create table Lot_medicament(
ref_medic VARCHAR2(10),
dateF DATE,
qte_lot NUMBER,
unite VARCHAR2(15),
remarque VARCHAR2(50));
CREATE VIEW myview(reference,libelle,vignette, date_peremption, Quantite,unite, Remarque) AS
SELECT M.ref_medic, libelle, vignette, dateF, Qte_lot, unite, LM.remarque
FROM Medicament M, Lot_medicament LM
WHERE M.ref_medic = LM.ref_medic
AND qte_lot > 0;
CREATE OR REPLACE TRIGGER instead_insert_in_myview
INSTEAD OF INSERT ON myview
DECLARE
med_ref Medicament.ref_medic%TYPE;
BEGIN
BEGIN
SELECT ref_medic into med_ref FROM Medicament where ref_medic = :new.reference;
EXCEPTION
WHEN NO_DATA_FOUND THEN
med_ref := NULL;
END;
IF med_ref IS NULL THEN
INSERT INTO Medicament(ref_medic, libelle, vignette, remarque, qtestock)
VALUES (:new.reference, :new.libelle, :new.vignette, :new.remarque, :new.Quantite);
END IF;
INSERT INTO Lot_medicament(ref_medic, dateF, qte_lot, unite, remarque)
VALUES (:new.reference, :new.date_peremption, :new.Quantite, :new.unite, :new.remarque);
END instead_insert_in_myview;
/
INSERT INTO myview VALUES
('12AS45','test','yes','06/06/2021',30,'boite','test');
then
select count(*) from Medicament;
gives you one row, and
select count(*) from Lot_medicament;
also gives you one row.
The mistake is writing :NEW.ref_med instead of :NEW.reference inside the trigger.
Thanks everybody .

Oracle Object containing list of same object

I'm currently struggling with a PL/SQL problem, and Ididn't find a clear answer yet. The problem is, I have an object of type T, which can contain a list of objects of same type T.
Let's say I have a type TPerson. TPerson is defined by a name, and a list of children. So I have a grandfather, his two sons, and the first one has 2 daughters.
In PL/SQL, after some researches, I did this :
CREATE OR REPLACE TYPE TPerson IS OBJECT
(
Name VARCHAR2(30),
Children REF TPersonList,
constructor function TPerson(name VARCHAR2) return self as result
);
CREATE OR REPLACE TYPE body TPerson as
constructor function TPerson(aname VARCHAR2) return self as result is
begin
Name := aname;
Children := TPersonList();
return;
end;
end;
CREATE OR REPLACE TYPE TPersonList IS TABLE OF REF TPerson';
Everything runs with no exception. But the TPerson type does not compile correctly, and I have this compilation error:
pls-00532 target of ref must be a complete or incomplete object type
It's the first time I use this REF thing. I'm not even sure I'm using it correctly. In my opinion, it's not a really good way to do that (the 'Children' thing), but I don't have the choice. So if someone could explain me a proper way to achieve that, that would help me a lot...
I'm not sure if an object type can have a member of collection of the same type as one have to be able to define the collection before the object type and object type before the collection. To break the circular dependency one needs to introduce a level of indirection in the following fashion (warning non-tested pseudocode follows):
-- supertype
create type object_t is object;
create type object_list_t is table of ref object_t;
-- subtype
create type person_t under object_t (
name varchar2(20)
,children object_list_t
);
And then in the implementation of person_t you use treat to narrow down from supertype to subtype.
Another idea is to have the relations of the objects in a specific relation table. See the example below.
First create a simple person-type:
create type person_t is object (
name varchar2(20)
);
/
show errors
create table persons of person_t;
insert into persons values(person_t('Grandfather'));
insert into persons values(person_t('Father'));
insert into persons values(person_t('Mother'));
insert into persons values(person_t('Son 1'));
insert into persons values(person_t('Daughter 1'));
insert into persons values(person_t('Son 2'));
Second create a table for parent-child relations:
create table x (
parent ref person_t
,child ref person_t
);
-- build a family tree
insert into x values(
(select ref(p) from persons p where name = 'Grandfather')
,(select ref(p) from persons p where name = 'Father')
);
insert into x values(
(select ref(p) from persons p where name = 'Father')
,(select ref(p) from persons p where name = 'Son 1')
);
insert into x values(
(select ref(p) from persons p where name = 'Father')
,(select ref(p) from persons p where name = 'Son 2')
);
insert into x values(
(select ref(p) from persons p where name = 'Father')
,(select ref(p) from persons p where name = 'Daughter 1')
);
insert into x values(
(select ref(p) from persons p where name = 'Mother')
,(select ref(p) from persons p where name = 'Son 1')
);
insert into x values(
(select ref(p) from persons p where name = 'Mother')
,(select ref(p) from persons p where name = 'Son 2')
);
insert into x values(
(select ref(p) from persons p where name = 'Mother')
,(select ref(p) from persons p where name = 'Daughter 1')
);
Now you can traverse the family tree with a good old SQL.
column parent format a30
column child format a30
select deref(child) as child from x where deref(parent).name = 'Father';
CHILD(NAME)
------------------------------
PERSON_T('Son 1')
PERSON_T('Son 2')
PERSON_T('Daughter 1')
select deref(parent) as parent, deref(child) as child
from x
start with deref(parent).name = 'Grandfather'
connect by prior child = parent
;
PARENT(NAME) CHILD(NAME)
------------------------------ ------------------------------
PERSON_T('Grandfather') PERSON_T('Father')
PERSON_T('Father') PERSON_T('Son 1')
PERSON_T('Father') PERSON_T('Son 2')
PERSON_T('Father') PERSON_T('Daughter 1')
The ref and deref functions are explained in Database Object-Relational Developer's Guide. That's a document you should be familiar with if you're working with Oracle object types.

Bulk ref cursor into nested table showing error

I Created a nested table
I Want to bulk ref cursor (C_get_cards) into the nested table created.
I Want to use nested table as one of the column in table STAT_QUERIES.
How do I combine all the three steps in a single procedure? Please correct me if my approach is wrong.
--Nested Table---
CREATE OR REPLACE TYPE T_card_details AS TABLE OF VARCHAR2(20) -- define type
/
CREATE TYPE Query AS OBJECT ( -- create object
CARD_QUERY T_card_details) -- declare nested table as attribute
/
CREATE TABLE STAT_QUERIES (
OBJECTTYPE VARCHAR2(50),
CATEGORY VARCHAR2(100),
CARD_QUERY T_card_details)
NESTED TABLE CARD_QUERY STORE AS CARD_QUERY_TAB;
select * FROM STAT_QUERIES
-- The cursor bulk collect into T_CARD details
CREATE OR REPLACE PROCEDURE NRMSP_INVENTORYSTATS IS
CURSOR C_get_cards
IS
SELECT
nt.name nt_name
,nd.name nd_name
,ct.name ct_name
,ps.name ps_name
,count(c.cardid)
FROM cardtype ct, card c, node n, nodetype nt, nodedef nd, provisionstatus ps
WHERE ct.name in ('SRA AMP', 'XLA AMP', 'SAM', 'ESAM')
AND ct.cardtypeid = c.card2cardtype
AND c.card2node = n.nodeid
AND n.node2nodetype = nt.nodetypeid
AND n.node2nodedef = nd.nodedefid
AND c.card2provisionstatus = ps.provisionstatusid
GROUP by nt.name, nd.name, ct.name, ps.name
;
TYPE cards_TAB_TYPE IS TABLE OF C_get_cards%ROWTYPE INDEX BY BINARY_INTEGER ;
T_card_details cards_TAB_TYPE ;
BEGIN
OPEN C_get_cards ;
FETCH C_get_cards
BULK COLLECT INTO T_card_details
LIMIT 250
;
END;
/
---INSERT INTO STAT_QUERIES---
INSERT INTO STAT_QUERIES
VALUES('CARD', 'Cards from Other Projects 2014', 'CARD_QUERY');

oracle triggers :new context variable

Question:
A Trigger which automatically stores in a separate table called ‘ExcellentSale’ the Sales Agent
name, car model and manufacturer name, each time the agreed price of a
SalesTransaction is more than 20% of the car’s asking price. (Note: You need to create
the ‘ExcellentSale’ table before implementing this trigger. To create the primary key, use a
sequence that starts at 1 and increments by 1).
I am using these tables
Manufacturer(manufacturerID, name, region)
Model(modelNo, name, type, previousModel, manufacturerID)
Car(VIN, dateAcquired, yearBuilt, purchasedPrice, askingPrice,
currentMileage, modelNo)
SalesAgent(agentID, name, DOB)
SalesTransaction(VIN, custID, agentID, dateOfSale, agreedPrice)
Here is my attempt
create sequence generateKey
start with 1
increment by 1;
CREATE TABLE ExcellentSale(
recordNo NUMBER,
agentName VARCHAR2(20) NOT NULL,
modelName VARCHAR2(20) NOT NULL,
manufacturerName VARCHAR2(20) NOT NULL,
PRIMARY KEY(recordNo));
create or replace trigger AutoStore
before insert on SalesTransaction
for each row
declare
SAname varchar2(50);
carModel varchar2(50);
manufacturerName varchar2(50);
askingprice number;
agreedprice number;
begin
select sa.name, mo.name, mu.name, c.askingprice, st.agreedprice
into SAname, CarModel, manufacturerName, askingprice, agreedprice
from manufacturer MU, Model MO, Car C, SalesAgent SA, SalesTransaction ST
where mu.manufacturerid = mo.manufacturerid
and st.vin = c.vin
AND c.vin = :new.vin
AND sa.agentID = :new.agentID;
IF :new.agreedPrice > (1.2 * askingPrice) THEN
INSERT INTO ExcellentSale
VALUES
(generateKey.nextval, agentName, modelName, manufacturerName);
END IF;
end AutoStore;
/
and the error I am getting is
PL/SQL: ORA -00984: Column not allowed here
please help, thank you in advanced.
In the VALUES clause of the insert statement, you have go use the variables that hold the values you need instead of the column names.
INSERT INTO ExcellentSale
VALUES
(generateKey.nextval, SAname, CarModel, manufacturerName);

Where does oracle store my object instances?

I've created the following two object types :
create or replace type person_typ as object (
person# varchar(10)
) not final;
create or replace type salesperson_typ under person_typ (
salesperson# varchar(10),
sSurname varchar(10),
sForename varchar(10),
dateOfBirth date
);
create table person_tab of person_typ (
person# primary key
);
And I've inserted a row using :
insert into person_tab
values (salesperson_typ('p1','s1', 'Jones', 'John', sysdate));
Which I can retrieve using the following :
select
treat(value(s) as salesperson_typ).person# as person_number,
treat(value(s) as salesperson_typ).sSurname as sSurname
from
person_tab s
;
However, if I look at person_tab I only see the following :
SQL> select * from person_tab;
PERSON#
----------
p1
I'm curious, where does the salesperson specific data get stored? I was almost expecting to find a salesperson table, but I can't find anything obvious.
Your object is stored invisibly in the same table.
You can check columns by querying USER_TAB_COLS:
SELECT *
FROM user_tab_cols
WHERE table_name = 'PERSON_TAB';
Then you can then use the column names* you just discovered in a query (except SYS_NC_ROWINFO$, that throws an error for me).
SELECT SYS_NC_OID$
,SYS_NC_TYPEID$
--,SYS_NC_ROWINFO$
,PERSON#
,SYS_NC00005$
,SYS_NC00006$
,SYS_NC00007$
,SYS_NC00008$
FROM PERSON_TAB;
Note*
You should not use these column names in any application because they are internal and subject to change in future patches/releases.

Resources