Below are the object types I have. Basically I have a person table and a child table as a nested table of person table.
I have a School table with a M:N Relationship with child table (nested). So I'm creating a intermediate table to insert child_school data.
How can I create that intermediate table and insert data?
create type school_t as object(
sid number(5,2),
name varchar(20))
/
create type child_t as object(
cid number(5,2),
name varchar(20))
/
create type childtable_t as table of child_t
/
create type person_t as object(
pid number(5,2),
name varchar(20),
child childtable_t)
/
create table person_tab of person_t(
pid primary key
)nested table child store as child_table
/
create table school_tab of school_t
/
--there's some problem. Below does not work.
create type school_child_t as object(
cid ref person_t,
sid ref school_t)
/
create table school_child_tab of school_child_t(
cid references person_tab,
sid references school_tab
)
/
--Here's what I want to do
create table school_child_tab(
cid number(5,2) references childtable_t,
sid number(5,2) references school_tab
)
/
cid reference should be the cid in nested table. The problem is referring it.
I saw your edit, and I was about to tell you it is impossible to reference a nested table externally.
The nested table is physically created as a distinct table that holds data separately from the parent table:
SQL> SELECT object_name, object_type
2 FROM all_objects
3 WHERE created > trunc(sysdate)
4 AND object_type = 'TABLE';
OBJECT_NAME OBJECT_TYPE
------------------------------ -------------------
SCHOOL_TAB TABLE
CHILD_TABLE TABLE
PERSON_TAB TABLE
Here you can see that Oracle has created a CHILD_TABLE table, however it is hidden from us and can only be worked internally by Oracle:
SQL> select * from child_table;
ORA-22812: cannot reference nested table column's storage table
In this case I was pretty sure that you couldn't reference the child table in any way, however to my surprise this seems to work (we can't select from CHILD_TABLE, however we can reference to it):
SQL> alter table child_table add constraint pk_child_table primary key (cid);
Table altered
SQL> CREATE TABLE school_child_tab (
2 cid REFERENCES child_table,
3 sid REFERENCES school_tab
4 );
Table created
You could build your inserts like this (I don't really like to store to store data as objects, but here you go):
SQL> insert into school_tab values (school_t(1, 'school A'));
1 row inserted
SQL> insert into person_tab values (
2 person_t(1, 'person A', childtable_t(child_t(1, 'child A'))));
1 row inserted
SQL> insert into school_child_tab values (1, 1);
1 row inserted
I have slightly altered your data model:
SQL> create type school_t as object(
2 sid number(5,2),
3 name varchar(20))
4 /
Type created.
SQL> create type child_t as object(
2 cid number(5,2),
3 name varchar(20))
4 /
Type created.
SQL> create table school_tab of school_t
2 /
Table created.
SQL> create table child_tab of child_t
2 /
Table created.
SQL>
Let's populate the nested tables:
SQL> insert into child_tab
2 values (111, 'Fred')
3 /
1 row created.
SQL> insert into child_tab
2 values (112, 'Ayesha')
3 /
1 row created.
SQL> insert into child_tab
2 values (113, 'Aadil')
3 /
1 row created.
SQL> insert into school_tab
2 values (222, 'Bash Street')
3 /
1 row created.
SQL> insert into school_tab
2 values (223, 'Greyfriars')
3 /
1 row created.
SQL>
Here is a nested table:
SQL> create type school_child_t as object(
2 cid ref child_t,
3 sid ref school_t)
4 /
Type created.
SQL> create table school_child_tab of school_child_t
2 /
Table created.
SQL>
We populate the intersection table like this:
SQL> insert into school_child_tab
2 select cid, sid
3 from
4 ( select ref(c) as cid from child_tab c where c.cid = 111 )
5 , ( select ref(s) as sid from school_tab s where s.sid = 222 )
6 /
1 row created.
SQL> insert into school_child_tab
2 select cid, sid
3 from
4 ( select ref(c) as cid from child_tab c where c.cid = 112 )
5 , ( select ref(s) as sid from school_tab s where s.sid = 222 )
6 /
1 row created.
SQL> insert into school_child_tab
2 select cid, sid
3 from
4 ( select ref(c) as cid from child_tab c where c.cid = 113 )
5 , ( select ref(s) as sid from school_tab s where s.sid = 222 )
6 /
1 row created.
SQL> insert into school_child_tab
2 select cid, sid
3 from
4 ( select ref(c) as cid from child_tab c where c.cid = 113 )
5 , ( select ref(s) as sid from school_tab s where s.sid = 223 )
6 /
1 row created.
SQL>
Query back the results
SQL> select c.name as child_name
2 , s.name as school_name
3 from school_child_tab sc
4 join child_tab c
5 on ( ref(c) = sc.cid )
6 join school_tab s
7 on ( ref(s) = sc.sid )
8 /
CHILD_NAME SCHOOL_NAME
-------------------- --------------------
Fred Bash Street
Ayesha Bash Street
Aadil Greyfriars
Aadil Bash Street
SQL>
Of course, that raises a question: if you're going to use the object's REF do you need the ID column? Certainly I think it is misleading to have a attribute called CID of type NUMBER for the CHILD_T type and an attribute with the same name but a datatype of REF for the SCHOOL_CHILD_T type.
Related
I created a primitive table in which I can store date, time, the id of a patient and the id of an employee.
CREATE TABLE Squedule (
id INTEGER,
datee DATE NOT NULL,
timee CHAR(5),
patientId CHAR(10),
employeeId CHAR(10),
CONSTRAINT squedule_pk PRIMARY KEY (kennzahl),
CONSTRAINT squedule_fkSprstdh FOREIGN KEY (employeeId) REFERENCES Sprechstdhilfe,
CONSTRAINT squedule_fkPatientIn FOREIGN KEY (patientId) REFERENCES PatientIn);
------------------I insert the data like so
INSERT INTO squedule(datee,timee,patientid,employeeid) VALUES('02.02.3000','16:43',8137770103,3146213220);
------------------This is the trigger for the id
CREATE TRIGGER newSquedule BEFORE INSERT ON Squedule
FOR EACH ROW
BEGIN
SELECT auto_increment_pk.NEXTVAL
INTO :new.id
FROM dual;
END;
This worked just fine until I noticed that no matter what data I try to insert for the datee value it always ends up with '03.01.2022' in the table for every single insert.
I dropped all tables and created them again a few times, because a few system_sequences got created, I guess because of an Identity I use in another table. But they did not get deleted with dropping every single table.
Maybe one of them is triggering something.
What can I do to solve this problem? I am clueless...
I can't reproduce what you're saying.
I removed foreign keys from the table (as I didn't feel like creating tables you reference).
SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> CREATE SEQUENCE auto_increment_pk;
Sequence created.
SQL> CREATE TABLE squedule
2 (
3 id INTEGER,
4 datee DATE NOT NULL,
5 timee CHAR (5),
6 patientid CHAR (10),
7 employeeid CHAR (10),
8 CONSTRAINT squedule_pk PRIMARY KEY (id)
9 );
Table created.
SQL> CREATE OR REPLACE TRIGGER newsquedule
2 BEFORE INSERT
3 ON squedule
4 FOR EACH ROW
5 BEGIN
6 :new.id := auto_increment_pk.NEXTVAL;
7 END;
8 /
Trigger created.
Inserts:
SQL> INSERT INTO squedule (datee,
2 timee,
3 patientid,
4 employeeid)
5 VALUES ('02.02.3000',
6 '16:43',
7 8137770103,
8 3146213220);
1 row created.
But, the way you put it, I can insert anything into the timee column, including such a rubbish:
SQL> INSERT INTO squedule (datee,
2 timee,
3 patientid,
4 employeeid)
5 VALUES ('25.02.3000',
6 'xy:83',
7 8137770103,
8 3146213220);
1 row created.
Result:
SQL> SELECT * FROM squedule;
ID DATEE TIMEE PATIENTID EMPLOYEEID
---------- ------------------- ----- ---------- ----------
1 02.02.3000 00:00:00 16:43 8137770103 3146213220 --> 02.02.3000, not 03.01.2022
2 25.02.3000 00:00:00 xy:83 8137770103 3146213220 --> invalid time value
SQL>
I suggest you abandon what you're doing and use only one column whose datatype is DATE as - in Oracle - it contains both date and time component. Something like this:
SQL> DROP TABLE squedule;
Table dropped.
SQL> CREATE TABLE squedule
2 (
3 id INTEGER,
4 datee DATE NOT NULL,
5 patientid CHAR (10),
6 employeeid CHAR (10),
7 CONSTRAINT squedule_pk PRIMARY KEY (id)
8 );
Table created.
SQL> CREATE OR REPLACE TRIGGER newsquedule
2 BEFORE INSERT
3 ON squedule
4 FOR EACH ROW
5 BEGIN
6 :new.id := auto_increment_pk.NEXTVAL;
7 END;
8 /
Trigger created.
Insert:
SQL> INSERT INTO squedule (datee, patientid, employeeid)
2 VALUES (TO_DATE ('02.02.3000 14:43', 'dd.mm.yyyy hh24:mi'),
3 8137770103,
4 3146213220);
1 row created.
SQL> SELECT * FROM squedule;
ID DATEE PATIENTID EMPLOYEEID
---------- ------------------- ---------- ----------
3 02.02.3000 14:43:00 8137770103 3146213220
SQL>
This is my user defined Type which i have created.
create or Replace TYPE cust_address_typ_new AS OBJECT
( add_id NUMBER,
street_address VARCHAR2(40)
, postal_code VARCHAR2(10)
, city VARCHAR2(30)
, state_province VARCHAR2(10)
, country_id CHAR(2)
);
and the below is the table of new type
CREATE TABLE address_table OF cust_address_typ_new;
Now i created another table as below
CREATE TABLE customer_addresses (
add_id NUMBER,
address REF cust_address_typ_new
SCOPE IS address_table);
and now i'm trying to insert values into customer_addresses table
insert into customer_addresses
values(1,SYSTEM.CUST_ADDRESS_TYP_NEW(1,'hi','87987','city','state',''))
Using sqlcl (Oracle 12.1):
create or Replace TYPE cust_address_typ_new AS OBJECT (
add_id NUMBER
, street_address VARCHAR2(40)
, postal_code VARCHAR2(10)
, city VARCHAR2(30)
, state_province VARCHAR2(10)
, country_id CHAR(2)
);
/
-- Type CUST_ADDRESS_TYP_NEW compiled
CREATE TABLE address_table OF cust_address_typ_new ;
-- Table ADDRESS_TABLE created.
CREATE TABLE customer_addresses (
add_id NUMBER
, address REF cust_address_typ_new SCOPE IS address_table
);
-- Table CUSTOMER_ADDRESSES created.
Errors
insert into customer_addresses
values ( 1, SYSTEM.CUST_ADDRESS_TYP_NEW(1,'hi','87987','city','state','') ) ;
-- ORA-00904: "SYSTEM"."CUST_ADDRESS_TYP_NEW": invalid identifier
insert into customer_addresses
values ( 1, CUST_ADDRESS_TYP_NEW( 1,'hi','87987','city','state','' ) ) ;
-- ORA-00932: inconsistent datatypes: expected REF ...CUST_ADDRESS_TYP_NEW got ...CUST_ADDRESS_TYP_NEW
From the documentation:
REF takes as its argument a correlation variable (table alias)
associated with a row of an object table or an object view. A REF
value is returned for the object instance that is bound to the
variable or row.
The following examples my help you:
-- {1} insert into the address_table ( 3 examples )
insert into address_table
values ( 1,'hi','87987','city','state','' ) ;
-- 1 row inserted.
insert into address_table
values( cust_address_typ_new( 2,'hi again','12345','city2','state2','' ) ) ;
-- 1 row inserted
insert into address_table
values( new cust_address_typ_new( 3,'hmpf','23456','city3','state3','' ) ) ;
-- 1 row inserted.
-- result
SQL> select * from address_table;
ADD_ID STREET_ADDRESS POSTAL_CODE CITY STATE_PROVINCE COUNTRY_ID
1 hi 87987 city state NULL
2 hi again 12345 city2 state2 NULL
3 hmpf 23456 city3 state3 NULL
Maybe you don't need the sequence (just added to fill the add_id column in the customer_addresses table).
-- {2} select from the address_table, use ref(),
-- insert into customer_addresses
SQL> create sequence ca_seq start with 1000 increment by 1;
Sequence CA_SEQ created.
insert into customer_addresses
select ca_seq.nextval, ref(AT) from address_table AT ;
-- result
select * from customer_addresses;
ADD_ID
----------
ADDRESS
------------------------------------------------------------------------
1000
2202086FC2AC912CD41038E0530100007F525D6FC2AC912CCD1038E0530100007F525D
1001
2202086FC2AC912CD51038E0530100007F525D6FC2AC912CCD1038E0530100007F525D
1002
2202086FC2AC912CD61038E0530100007F525D6FC2AC912CCD1038E0530100007F525D
How can I properly sort the results to show the results in the following order
1st - Parent Name
2nd - Child Name
3rd - Teacher Name
4th - Other Name
Where the parent name and child name is on the same table Family
id | parent name | child name
-----------------------------
1 Denz Hanz
2 Denz Pog
3 Joann Mac
while the other names are on different tables teacher table
id | teacher name
-----------------
1 Miguel
2 Sean
and Other_guest table
id | guest name
-----------------
1 Mike
2 Mal
where in cases that the parent did not arrive, the child name will be showed. Result of the query should show something like this
Participant Name
----------------
1. Denz
2. Denz
3. Mac
4. Miguel
5. Sean
6. Mal
7. Mike
I tried using order by field(),order by field asc, field2 dec ... etc but it seems not the result we wanted.
Below query works in most of the databases, this query will first sort by type and then name:
select * from (
select parent_name, 0 sort_by from table1
union all
select chile_name, 1 from table1
union all
select teacher_name, 2 from table2
union all
select guest_name, 3 from table3) t
order by sort_by, parent_name;
Here is my solution for Oracle DB:
Note that the UNION does not allow ORDER BY clause in subsequent select statements, therefore a procedure is necessary that generates the list of guests in the GUESTS table.
The table ARRIVED contains the IDs of the arrived parents, all other tables are self explanatory.
create table Family (id number, parentname varchar2(100), childname varchar2(100));
create table Teacher (id number, teachername varchar2(100));
create table Other_guest (id number, guestname varchar2(100));
create table Arrived (id number);
create table Guests (guestname varchar2(100));
create sequence Family_seq start with 1 order;
create sequence Teacher_seq start with 1 order;
create sequence Other_seq start with 1 order;
create or replace trigger Family_id before insert on Family for each row
begin
select Family_seq.nextval into :new.id from dual;
end;
create or replace trigger Teacher_id before insert on Teacher for each row
begin
select Teacher_seq.nextval into :new.id from dual;
end;
create or replace trigger Other_id before insert on Other_guest for each row
begin
select Other_seq.nextval into :new.id from dual;
end;
insert into Family (parentname, childname) values ('Denz', 'Hanz');
insert into Family (parentname, childname) values ('Denz', 'Pog');
insert into Family (parentname, childname) values ('Joann', 'Mac');
insert into Teacher (teachername) values ('Miguel');
insert into Teacher (teachername) values ('Sean');
insert into Other_guest (guestname) values ('Mike');
insert into Other_guest (guestname) values ('Mal');
insert into Arrived (id) values (1);
insert into Arrived (id) values (2);
create or replace procedure update_guest_list as
pragma autonomous_transaction;
cursor c_parents is select parentname from family where id in (select id from arrived) order by family.parentname;
cursor c_children is select childname from family where id not in (select id from arrived) order by family.childname;
cursor c_teachers is select teachername from teacher order by teacher.teachername asc;
cursor c_others is select guestname from other_guest order by other_guest.guestname asc;
begin
delete from guests;
for parents_rec in c_parents
loop
insert into guests (guestname) values (parents_rec.parentname);
end loop;
for children_rec in c_children
loop
insert into guests (guestname) values (children_rec.childname);
end loop;
for teachers_rec in c_teachers
loop
insert into guests (guestname) values (teachers_rec.teachername);
end loop;
for others_rec in c_others
loop
insert into guests (guestname) values (others_rec.guestname);
end loop;
commit;
end;
/
execute update_guest_list;
How do you do multiple insert with SQL in Oracle 12c when you have an identity column?
INSERT ALL
INTO Table1 (Column2) Values (1)
INTO Table1 (Column2) Values (2)
SELECT * FROM dual;
where Table1 has column1 as an identity, will set the identity column to have the same value which violates the primary key constraint.
CREATE TABLE Table1 (
Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
column2 VARCHAR2(255),
column3 NUMBER,
PRIMARY KEY (Table1Id)
);
INSERT ALL
INTO Table1 (column2, column3) VALUES ('a', '1')
INTO Table1 (column2, column3) VALUES ('b', '2')
SELECT * FROM dual;
--SQL Error: ORA-00001: unique constraint violated
What am I doing wrong with this?
EDIT Added two test cases, and a possible workaround.
Though Insert statement and insert all statement are practically the same conventional insert statement. But when it comes to sequences, they work differently.
Test case 1 : Identity columns
SQL> DROP TABLE table1 PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE Table1 (
2 Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
3 column3 NUMBER,
4 PRIMARY KEY (Table1Id)
5 );
Table created.
SQL>
SQL> INSERT ALL
2 INTO Table1 (column3) VALUES ('1')
3 INTO Table1 (column3) VALUES ('2')
4 SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.SYS_C0010439) violated
SQL>
Let's see what's actually happening under the hood -
SQL> CREATE TABLE Table1 (
2 Table1Id NUMBER GENERATED ALWAYS AS IDENTITY,
3 column3 NUMBER,
4 CONSTRAINT A UNIQUE (Table1Id)
5 );
Table created.
SQL> INSERT ALL
2 INTO Table1 (column3) VALUES (1)
3 INTO Table1 (column3) VALUES (2)
4 SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.A) violated
SQL> SELECT * FROM table1;
no rows selected
SQL> ALTER TABLE table1
2 DISABLE CONSTRAINT a;
Table altered.
SQL> INSERT ALL
2 INTO Table1 (column3) VALUES (1)
3 INTO Table1 (column3) VALUES (2)
4 SELECT * FROM dual;
2 rows created.
SQL> SELECT * FROM table1;
TABLE1ID COLUMN3
---------- ----------
2 1
2 2
SQL>
So, the sequence progressed to nextval however there was an unique constraint violation the first time we did an Insert All. Next, we disabled the unique constraint, and the subsequent Insert All reveals that the sequence did not progress to nextval, rather it attempted to insert duplicate keys.
Though the issue doesn't occur with a INSERT-INTO-SELECT statement.
SQL> INSERT INTO table1(column3) SELECT LEVEL FROM dual CONNECT BY LEVEL <=5;
5 rows created.
SQL>
SQL> SELECT * FROM table1;
TABLE1ID COLUMN3
---------- ----------
2 1
3 2
4 3
5 4
6 5
SQL>
Surprisingly, as per the metadata, the sequence is supposed to proceed to nextval automatically, however it doesn't happen with an Insert All statement.
SQL> SELECT COLUMN_NAME,
2 IDENTITY_COLUMN,
3 DATA_DEFAULT
4 FROM user_tab_cols
5 WHERE table_name ='TABLE1'
6 AND IDENTITY_COLUMN='YES';
COLUMN_NAME IDENTITY_COLUMN DATA_DEFAULT
--------------- --------------- ------------------------------
TABLE1ID YES "LALIT"."ISEQ$$_94458".nextval
SQL>
Test Case 2 : Using a sequence explicitly
The INSERT ALL would work the same way whether an identity column is used or an explicit sequence is used.
SQL> DROP SEQUENCE s;
Sequence dropped.
SQL>
SQL> CREATE SEQUENCE s;
Sequence created.
SQL>
SQL> DROP TABLE t PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE t (
2 ID NUMBER,
3 text VARCHAR2(50),
4 CONSTRAINT id_pk PRIMARY KEY (ID)
5 );
Table created.
SQL>
SQL> INSERT ALL
2 INTO t VALUES (s.nextval, 'a')
3 INTO t VALUES (s.nextval, 'b')
4 INTO t VALUES (s.nextval, 'c')
5 INTO t VALUES (s.nextval, 'd')
6 SELECT * FROM dual;
INSERT ALL
*
ERROR at line 1:
ORA-00001: unique constraint (LALIT.ID_PK) violated
SQL>
SQL> SELECT * FROM T;
no rows selected
SQL>
SQL> ALTER TABLE t
2 DISABLE CONSTRAINT id_pk;
Table altered.
SQL> INSERT ALL
2 INTO t VALUES (s.nextval, 'a')
3 INTO t VALUES (s.nextval, 'b')
4 INTO t VALUES (s.nextval, 'c')
5 INTO t VALUES (s.nextval, 'd')
6 SELECT * FROM dual;
4 rows created.
SQL> SELECT * FROM T;
ID TEXT
---------- ----------------------------------------
2 a
2 b
2 c
2 d
SQL>
Possible workaround - Using a ROW LEVEL trigger
SQL> CREATE OR REPLACE TRIGGER t_trg
2 BEFORE INSERT ON t
3 FOR EACH ROW
4 WHEN (new.id IS NULL)
5 BEGIN
6 SELECT s.NEXTVAL
7 INTO :new.id
8 FROM dual;
9 END;
10 /
Trigger created.
SQL> truncate table t;
Table truncated.
SQL> INSERT ALL
2 INTO t (text) VALUES ('a')
3 INTO t (text) VALUES ('b')
4 INTO t (text) VALUES ('c')
5 INTO t (text) VALUES ('d')
6 SELECT * FROM dual;
4 rows created.
SQL> SELECT * FROM t;
ID TEXT
---------- -------------------------
3 a
4 b
5 c
6 d
SQL>
Here's a workaround using the UNION ALL method instead of the INSERT ALL method. For some reason the data must be wrapped in a select * from (...) or it will generate the error ORA-01400: cannot insert NULL into ("JHELLER"."TABLE1"."TABLE1ID").
insert into table1(column2, column3)
select *
from
(
select 'a', '1' from dual union all
select 'b', '2' from dual
);
Need help in Oracle query
Requirement:
I have 5 rows in a table lets say ID = 1, 2, 3, 4, 5
Requirement is as such that user may pass any value as input and if that value is not there in table then query should return me the value which is not present.
Ex:
1. If user passes 9 then Oracle query should return the output as 9
2. If user passes 1,2,10 then Oracle query should return the output as 10
as 9 and 10 in above example are not in table.
I am using following query but not getting result.
SELECT ID
FROM TABLE_NAME WHERE ID NOT IN
(SELECT ID
FROM TABLE_NAME where ID NOT in (1,2,10))
create table z (id number);
Table created.
SQL> insert into z values (1);
1 row created.
SQL> insert into z values (2);
1 row created.
SQL> insert into z values (3);
1 row created.
SQL> insert into z values (4);
1 row created.
SQL> insert into z values (5);
1 row created.
SQL> select 10 id from dual
2 minus
3 select id from z;
ID
----------
10
You could use a nested table as input:
SQL> CREATE TABLE table_name (ID NUMBER NOT NULL);
Table created
SQL> INSERT INTO table_name (SELECT ROWNUM FROM dual CONNECT BY LEVEL <= 5);
5 rows inserted
SQL> CREATE TYPE tab_number AS TABLE OF NUMBER;
2 /
Type created
SQL> SELECT *
2 FROM TABLE(tab_number(1,2,10)) x
3 WHERE x.column_value NOT IN (SELECT ID FROM table_name);
COLUMN_VALUE
------------
10