Show film description with string format - sql

I have a task need to write a trigger statement with format
This is my database schema:
I write my code like
CREATE OR REPLACE TRIGGER "BI_FILM_DESP"
BEFORE INSERT ON "FILM"
FOR EACH ROW
DECLARE
RatingFilm VARCHAR2(8);
Seq NUMBER(3);
OriginalL VARCHAR2(20);
Language VARCHAR2(20);
BEGIN
SELECT RATING INTO RatingFilm FROM FILM F;
SELECT COUNT(RATING) INTO Seq FROM FILM F GROUP BY F.RATING;
SELECT LANGUAGE.NAME INTO OriginalL FROM LANGUAGE L WHERE (L.LANGUAGE_ID =: FILM.LANGUAGE_ID);
SELECT LANGUAGE.NAME INTO Language FRoM LANGUAGE L WHERE (L.LANGUAGE_ID =: FILM.LANGUAGE_ID);
SELECT CONCAT(RatingFilm, "-", Seq, ": Originally in", OriginalL, ". Re-released in ", Language, ".");
END;
/
However it shows error
I think it's hard to read those errors and I need some help to correct it. Thanks in advance.
Edit: Add code that create tables
CREATE TABLE film (
film_id NUMBER(5) NOT NULL,
title varchar2(255),
description varchar2(255),
release_year NUMBER(4) DEFAULT NULL,
language_id NUMBER(3) NOT NULL,
original_language_id NUMBER(3) DEFAULT NULL,
rental_duration NUMBER(3) DEFAULT 3 NOT NULL,
rental_rate NUMBER(4,2) DEFAULT '4.99' NOT NULL,
length NUMBER(5) DEFAULT NULL,
replacement_cost NUMBER(5,2) DEFAULT '19.99' NOT NULL,
rating varchar2(8) DEFAULT 'G' NOT NULL,
special_features varchar2(255) DEFAULT NULL
);
CREATE TABLE language (
language_id NUMBER(3) NOT NULL,
name varchar2(20)
);

I hope you read astentx's comment.
Here's how you could/should do it.
Tables involved (with necessary columns only):
SQL> CREATE TABLE language
2 (
3 language_id NUMBER PRIMARY KEY,
4 name VARCHAR2 (20)
5 );
Table created.
SQL> INSERT INTO language (language_id, name)
2 SELECT 1, 'English' FROM DUAL
3 UNION ALL
4 SELECT 2, 'Croatian' FROM DUAL;
2 rows created.
SQL> CREATE TABLE film
2 (
3 film_id NUMBER PRIMARY KEY,
4 title VARCHAR2 (20),
5 description VARCHAR2 (100),
6 language_id NUMBER REFERENCES language,
7 original_language_id NUMBER REFERENCES language,
8 rating NUMBER
9 );
Table created.
Trigger: don't select from table on which that row-level trigger fires as you'd get mutating table error. Good for you, you don't have to do that - use :new pseudorecord values instead:
SQL> CREATE OR REPLACE TRIGGER bi_film_desp
2 BEFORE INSERT
3 ON film
4 FOR EACH ROW
5 DECLARE
6 l_language language.name%TYPE;
7 l_original_language language.name%TYPE;
8 BEGIN
9 SELECT l.name
10 INTO l_language
11 FROM language l
12 WHERE l.language_id = :new.language_id;
13
14 SELECT l.name
15 INTO l_original_language
16 FROM language l
17 WHERE l.language_id = :new.original_language_id;
18
19 :new.description :=
20 'Rating: '
21 || :new.rating
22 || ', original language: '
23 || l_original_language
24 || ', language: '
25 || l_language;
26 END;
27 /
Trigger created.
SQL>
Let's test it:
SQL> INSERT INTO film (film_id,
2 title,
3 language_id,
4 original_language_id,
5 rating)
6 VALUES (1,
7 'Izbavitelj',
8 1,
9 2,
10 7);
1 row created.
Result:
SQL> select title, description, rating from film;
TITLE DESCRIPTION RATING
---------- ------------------------------------------------------------ ----------
Izbavitelj Rating: 7, original language: Croatian, language: English 7
SQL>
Looks OK to me.

Related

Creating a table gives a "missing right parenthesis" error

I have been getting error "ORA-00907: missing right parenthesis" when I run this create table statement:
create table employee(
primary key(emp_id number(20)),
emp_name varchar(30),
birth_date date CHECK(birth_date>18),
gender varchar(10),
dept_no number(20)
CONSTRAINT fk FOREIGN KEY(dept_no)
REFERENCES department(dept_no),
address varchar(50),
designation varchar(20)
CHECK(designation IN('manager', 'clerk', 'leader', 'analyst', 'designer', 'coder','tester')),
salary number(50)
CHECK(salary>0),
experience number(2),
email_id varchar(30)
CONSTRAINT chk_email
CHECK (REGEXP_LIKE(email_id,'^[A-Za-z0-9_.]+#[A-Za-z]+\.[A-Za-z]{2,4}$'))
);
I have looked up the exact syntax and after checking many times, everything seems to be just perfect but the error still exists. What is wrong?
A little bit of
invalid syntax (position of the primary key keywords),
superfluous foreign key keywords (you'd use them out of line, not inline),
check constraint for the birth_date column is wrong (how can date be larger than 18?),
Oracle suggests us to use varchar2 instead of varchar,
number(50) has too large precision (perhaps you'd rather just skip it).
Once fixed (with a dummy master table):
SQL> CREATE TABLE department
2 (
3 dept_no NUMBER PRIMARY KEY
4 );
Table created.
Employee:
SQL> CREATE TABLE employee
2 (
3 emp_id NUMBER (20) PRIMARY KEY,
4 emp_name VARCHAR2 (30),
5 birth_date DATE,
6 gender VARCHAR2 (10),
7 dept_no NUMBER CONSTRAINT fk_emp_dept REFERENCES department (dept_no),
8 address VARCHAR2 (50),
9 designation VARCHAR2 (20)
10 CHECK
11 (designation IN ('manager',
12 'clerk',
13 'leader',
14 'analyst',
15 'designer',
16 'coder',
17 'tester')),
18 salary NUMBER CHECK (salary > 0),
19 experience NUMBER (2),
20 email_id VARCHAR2 (30)
21 CONSTRAINT chk_email CHECK
22 (REGEXP_LIKE (
23 email_id,
24 '^[A-Za-z0-9_.]+#[A-Za-z]+\.[A-Za-z]{2,4}$'))
25 );
Table created.
SQL>
As of a trigger that checks employee's age, here's how:
SQL> CREATE OR REPLACE TRIGGER trg_bi_emp
2 BEFORE INSERT
3 ON employee
4 FOR EACH ROW
5 BEGIN
6 IF MONTHS_BETWEEN (SYSDATE, :new.birth_date) < 18 * 12
7 THEN
8 raise_application_error (-20000,
9 'Too young; must be at least 18 years of age');
10 END IF;
11 END;
12 /
Trigger created.
SQL> INSERT INTO employee (emp_id, birth_date) VALUES (1, DATE '2020-07-25');
INSERT INTO employee (emp_id, birth_date) VALUES (1, DATE '2020-07-25')
*
ERROR at line 1:
ORA-20000: Too young; must be at least 18 years of age
ORA-06512: at "SCOTT.TRG_BI_EMP", line 4
ORA-04088: error during execution of trigger 'SCOTT.TRG_BI_EMP'
SQL> INSERT INTO employee (emp_id, birth_date) VALUES (1, DATE '1997-07-25');
1 row created.
SQL>

Inserting random from a given string

My purpose is to write an insert statement that adds a random value from a given string, more specifically if the person is married, single or a widower. I needed to first populate my table with hundreds of random entries because I need to run Autotrace on it and check its optimization. In any event, I first created the table, then populated all the columns, except for the one concerning marital status. Then I wanted to write a different INSERT statement just for that because I thought it might be easier then putting it into the plSQL block I made to populate the table. Here is my code:
create table employees(
id_employee integer not null,
name varchar2(50) not null,
surname varchar2(50) not null,
marital_status varchar2(50),
birthday date);
ALTER TABLE employees ADD CONSTRAINT employees_pk PRIMARY KEY ( id_employee );
declare
id_employee integer:= 1;
begin
while id_employee <= 20 loop
insert into employees values (id_employee, dbms_random.string('l', 10), dbms_random.string('l', 10), null, TO_DATE(
TRUNC(
DBMS_RANDOM.VALUE(TO_CHAR(DATE '2016-01-01','J')
,TO_CHAR(DATE '2020-12-31','J')
)
),'J'
));
id_employee:= id_employee + 1;
end loop;
end;
Now that I've managed this and it worked, I just need to create an INSERT statement to basically modify the null value that is on every entry's marital_status column. But as I stated earlier, I want to have a statement that will return either (married, single, widower). Can this be achieved? Or is it not possible to give a command to extract a random value from a given string?
If I understood you correctly, you don't need PL/SQL at all - everything can be done in a single insert statement (or select, if you prefer). Here's how:
SQL> CREATE TABLE employees
2 (
3 id_employee INTEGER PRIMARY KEY,
4 name VARCHAR2 (50) NOT NULL,
5 surname VARCHAR2 (50) NOT NULL,
6 marital_status VARCHAR2 (50),
7 birthday DATE
8 );
Table created.
SQL> CREATE SEQUENCE seqes;
Sequence created.
Insert sample rows:
SQL> INSERT INTO employees (id_employee,
2 name,
3 surname,
4 marital_status,
5 birthday)
6 SELECT seqes.NEXTVAL id_employee,
7 DBMS_RANDOM.string ('l', 10) name,
8 DBMS_RANDOM.string ('l', 10) surname,
9 --
10 CASE
11 WHEN MOD (seqes.NEXTVAL, 3) = 0 THEN 'single'
12 WHEN MOD (seqes.NEXTVAL, 2) = 0 THEN 'married'
13 ELSE 'widower'
14 END marital_status,
15 --
16 TO_DATE (
17 TRUNC (
18 DBMS_RANDOM.VALUE (TO_CHAR (DATE '2016-01-01', 'J'),
19 TO_CHAR (DATE '2020-12-31', 'J'))),
20 'J') birthda
21 FROM DUAL
22 CONNECT BY LEVEL <= 20;
20 rows created.
Result:
SQL> SELECT * FROM employees;
ID_EMPLOYEE NAME SURNAME MARITAL_STATUS BIRTHDAY
----------- ------------ ------------ --------------- ----------
1 cpewxypfop urkqkpapdk widower 22.12.2018
2 qjslhprqxf jxoaennyqe married 27.08.2017
3 jjwknqkcel zkmnwtoovv single 25.10.2018
4 levwydigey numbxjvjtc married 12.02.2019
5 hswtiotjin cjdfiastvi widower 30.01.2019
6 yxahvjfmre dnlfmkphmv single 11.08.2017
7 nctcntredz raqpofzufx widower 29.05.2018
8 wyivovpnoc ikjakuzanf married 19.09.2016
9 rvtbqfgqnu iuqjqosait single 28.07.2018
10 oislloosfy xtfxpnceik married 30.03.2020
11 issbxtldsn bovdghpjke widower 21.09.2018
12 gzgjdlvwcw rmfqglwohc single 11.12.2019
13 utbznnyyhs ojcswdwuvh widower 21.12.2019
14 etsvhavose fypgntictn married 23.03.2018
15 myjijmagej lvmpbcvcfc single 12.09.2016
16 givwwayxkf hgemcfvnff married 13.02.2016
17 nquarbpzlf zwgjukhgxg widower 07.09.2018
18 lnyyrkohac ttygaxmvle single 25.05.2020
19 gmqboujcbb qszmifozcs widower 20.09.2019
20 eegwdvqvld dsembshumq married 04.09.2020
20 rows selected.
SQL>

I have a problem with triggers i sql Oracle

(zaposlenik means emplpoyee, godisnji_odmor means vacation)
So I want to make a trigger that triggers if an employee(here is called zaposlenik) has more than 21 days of vacation after an insert in year 2020. An employee can have multiple vacations(like 2 days, 10 days...) one to many relation between tables zaposlenik(employee) and godisnji_odmor(vacation). My problem here is that I don't know how to get that zaposlenik_id from the insert on which the trigger tiggers so I can sum up the vacation days of that employee on the ID = zaposlenik_id.
Here are my tables and trigger.
CREATE TABLE zaposlenik
(
zaposlenik_id INTEGER CONSTRAINT zaposlenik_pk PRIMARY KEY,
posao VARCHAR(30) NOT NULL,
ime VARCHAR(30) NOT NULL,
prezime VARCHAR(30) NOT NULL,
broj_tel INTEGER NOT NULL,
email VARCHAR(50) NOT NULL,
adresa VARCHAR(100) NOT NULL,
mjesecni_iznos_place FLOAT NOT NULL,
IBAN VARCHAR(34) NOT NULL,
budzet FLOAT,
parking_mjesto_id VARCHAR(5) CONSTRAINT zaposlenik_parking_mjesto_fk REFERENCES
parking_mjesto(etaza_i_br_mjesta),
zaposlenik_id_2 INTEGER CONSTRAINT zaposlenik_zaposlenik_fk REFERENCES
zaposlenik(zaposlenik_id)
);
CREATE TABLE godisnji_odmor
(
godisnji_odmor_id INTEGER CONSTRAINT godisnji_odmor_pk PRIMARY KEY,
pocetak DATE NOT NULL,
kraj DATE NOT NULL,
zaposlenik_id INTEGER NOT NULL CONSTRAINT godisnji_odmor_zaposlenik_fk REFERENCES
zaposlenik(zaposlenik_id)
);
CREATE OR REPLACE TRIGGER t_godisnji
AFTER INSERT
ON godisnji_odmor
DECLARE
v_br NUMBER; --sum of the vacation days
v_id NUMBER; -- id of the employee that is inserted (his id is zaposlenik_id)
BEGIN
SELECT zaposlenik_id INTO v_id FROM INSERTED;
SELECT SUM( g.kraj - g.pocetak ) INTO v_br
FROM zaposlenik z INNER JOIN godisnji_odmor g USING(zaposlenik_id)
WHERE g.pocetak > '01-JANUARY-2020' AND zaposlenik_id = v_id;
IF v_br > 21 THEN
ROLLBACK;
raise_application_error(-20100,'Godisnji prekoracuje 21 dan u sumi');
END IF;
END t_godisnji;
/
You need to use the :new for this as follows:
replace
SELECT zaposlenik_id INTO v_id FROM INSERTED;
with
v_id := :new.zaposlenik_id;
In short, You can have your code without v_id variable as follows:
CREATE OR REPLACE TRIGGER t_godisnji
AFTER INSERT
ON godisnji_odmor
DECLARE
v_br NUMBER; --sum of the vacation days
--v_id NUMBER; -- id of the employee that is inserted (his id is zaposlenik_id)
BEGIN
--SELECT zaposlenik_id INTO v_id FROM INSERTED;
SELECT SUM( g.kraj - g.pocetak ) INTO v_br
FROM zaposlenik z INNER JOIN godisnji_odmor g USING(zaposlenik_id)
WHERE g.pocetak > '01-JANUARY-2020'
AND zaposlenik_id = :new.zaposlenik_id; -- see the usage of :new here
IF v_br > 21 THEN
ROLLBACK;
raise_application_error(-20100,'Godisnji prekoracuje 21 dan u sumi');
END IF;
END t_godisnji;
/

How to count the number of line without repetition of a set value in a PROCEDURE

Oracle SQL+ (PL/SQL) is used*
How do I count the number of ligne in a similar case, using a PROCEDURE:
Example of a table
CREATE TABLE Teacher
(name CHAR(7) NOT NULL,
lastName INTEGER NOT NULL,
teacherCode CHAR(5) NOT NULL,
CONSTRAINT PrimaryKey PRIMARY KEY (name)
);
CREATE TABLE Class
(acronym CHAR(7) NOT NULL,
groupNum INTEGER NOT NULL,
sessionCode INTEGER NOT NULL,
teacherCode CHAR(5) NOT NULL,
CONSTRAINT P_Key PRIMARY KEY (acronym)
CONSTRAINT F_Key FOREIGN KEY (teacherCode)
);
I want to count the number of classes a particular teacher gave for each session.
Example of data in the table
Teacher
John Jonas JJ000
zzzz zzzzz zzzzz
zzzz zzzzz zzzzz
...
Class
XXX1000 10 1000 JJ000
zzzzzzz zz zzzz zzzzz
XXX3333 16 2000 JJ000
zzzzzzz zz zzzz zzzzz
XXX2222 12 1000 JJ000
zzzzzzz zz zzzz zzzzz
XXX1000 13 2000 JJ000
zzzzzzz zz zzzz zzzzz
XXX9999 16 2000 JJ000
I would like to have the following print:
Code of the teacher: JJ000
Name: John
Last name: Jonas
Number of classes given in the session 1000: 2
Number of classes given in the session 2000: 3
Here's how my PROCEDURE looks so far:
CREATE OR REPLACE PROCEDURE numberOfGiverClasses
(codeOfTeacher Teacher.teacherCode%TYPE) IS
v_teacherCode Teacher.teacherCode%TYPE;
v_name Teacher.name%TYPE;
v_lastName Teacher.lastName%TYPE;
v_nbGivenClasses INTEGER;
v_sessionCode Class.sessionCode%TYPE;
BEGIN
SELECT teacherCode, name, lastName
INTO v_teacherCode, v_name, v_lastName
FROM Teacher
WHERE teacherCode = codeOfTeacher ;
DBMS_OUTPUT.PUT('Code of the teacher: ');
DBMS_OUTPUT.PUT_LINE(v_teacherCode);
DBMS_OUTPUT.PUT('Name: ');
DBMS_OUTPUT.PUT_LINE(v_name);
DBMS_OUTPUT.PUT('Last name: ');
DBMS_OUTPUT.PUT_LINE(v_lastName);
????
DBMS_OUTPUT.PUT('Number of classes given in the session ');
DBMS_OUTPUT.PUT(v_sessionCode);
DBMS_OUTPUT.PUT(': ');
DBMS_OUTPUT.PUT(v_nbGivenClasses);
END numberOfGiverClasses;
Welcome to SO!
Firstly, there appears to be multiple issues with your schema/syntax:
1. Teacher.lastName is declared as INTEGER - should be varchar instead?
2. Class.acronym is defined as primary key but has duplicates based on your data - should groupNum be the primary key instead?
3. F_Key is missing the referencing column
4. teacherCode isn't defined as a primary key in Teacher table
I've created the below sample schema after fixing these issues. Please check if this is what you intended
create table teacher
(
name varchar2 (10) not null,
lastname varchar2 (10) not null,
teachercode varchar2 (10) not null,
constraint primarykey primary key (teachercode)
);
create table class
(
acronym varchar2 (10) not null,
groupnum integer not null,
sessioncode integer not null,
teachercode varchar2 (10) not null,
constraint p_key primary key (groupnum),
constraint f_key foreign key (teachercode) references teacher (teachercode)
);
insert into teacher select 'John', 'Jonas', 'JJ000' from dual;
commit;
insert into class
select 'XXX1000' acronym, 10 groupnum, 1000 sessioncode, 'JJ000' teachercode from dual union all
select 'XXX3333', 16, 2000, 'JJ000' from dual union all
select 'XXX2222', 12, 1000, 'JJ000' from dual union all
select 'XXX1000', 13, 2000, 'JJ000' from dual union all
select 'XXX9999', 17, 2000, 'JJ000' from dual;
commit;
select * from teacher;
| NAME |LASTNAME |TEACHERCODE |
| John |Jonas |JJ000 |
select * from class;
|ACRONYM |GROUPNUM |SESSIONCODE |TEACHERCODE |
|XXX1000 |10 |1000 |JJ000 |
|XXX3333 |16 |2000 |JJ000 |
|XXX2222 |12 |1000 |JJ000 |
|XXX1000 |13 |2000 |JJ000 |
|XXX9999 |17 |2000 |JJ000 |
You may be able to print the count of classes per session using a cursor for loop inside the proc. You may modify the cursor as appropriate based on your requirement.
A sample is as follows ..
CREATE OR REPLACE PROCEDURE numberOfGiverClasses
(codeOfTeacher Teacher.teacherCode%TYPE) IS
v_teacherCode Teacher.teacherCode%TYPE;
v_name Teacher.name%TYPE;
v_lastName Teacher.lastName%TYPE;
BEGIN
SELECT teacherCode, name, lastName
INTO v_teacherCode, v_name, v_lastName
FROM Teacher
WHERE teacherCode = codeOfTeacher ;
DBMS_OUTPUT.PUT_LINE(rpad('Code of the teacher: ',25) || v_teacherCode);
DBMS_OUTPUT.PUT_LINE(rpad('Name: ',25) || v_name);
DBMS_OUTPUT.PUT_LINE(rpad('Last name: ',25) || v_lastName);
for o in (
select sessioncode, count(groupnum) cnt from class
where teachercode = v_teacherCode
group by sessioncode
) loop
DBMS_OUTPUT.PUT_LINE('Number of classes given in the session ' || o.sessionCode ||': ' ||o.cnt );
end loop;
exception when others then
dbms_output.put_line(sqlerrm);
END numberOfGiverClasses;
Sample execution
set serveroutput on size unlimited
begin
numberOfGiverClasses('JJ000');
end;
Output:
Code of the teacher: JJ000
Name: John
Last name: Jonas
Number of classes given in the session 1000: 2
Number of classes given in the session 2000: 3
If this doesn't work, please enhance your question with additional details
If this suits your requirement, feel free to accept and vote
You can achieve it using the GROUP BY in a single query as follows:
SQL> -- Your data
SQL> with teacher as
2 (select 'John' name, 'Jonas' lastname, 'JJ000' teachercode from dual),
3 class as
4 (select 'XXX1000' acronym, 10 groupnum, 1000 sessioncode, 'JJ000' teachercode from dual union all
5 select 'XXX3333', 16, 2000, 'JJ000' from dual union all
6 select 'XXX2222', 12, 1000, 'JJ000' from dual union all
7 select 'XXX1000', 13, 2000, 'JJ000' from dual union all
8 select 'XXX9999', 17, 2000, 'JJ000' from dual)
9 --
10 -- actual query starts from here
11 SELECT
12 'Code of the teacher: ' || TEACHERCODE || CHR(10)
13 || 'Name: ' || NAME || CHR(10)
14 || 'Last name: ' || LASTNAME || CHR(10)
15 || LISTAGG('Number of classes given in the session ' || SESSIONCODE || ' : ' || CNT, CHR(10))
16 WITHIN GROUP(ORDER BY CNT)
17 FROM ( SELECT T.TEACHERCODE, T.NAME, T.LASTNAME, C.SESSIONCODE, COUNT(1) AS CNT
18 FROM TEACHER T JOIN CLASS C ON T.TEACHERCODE = C.TEACHERCODE
19 GROUP BY T.TEACHERCODE, T.NAME, T.LASTNAME, C.SESSIONCODE
20 )
21 GROUP BY TEACHERCODE, NAME, LASTNAME;
'CODEOFTHETEACHER:'||TEACHERCODE||CHR(10)||'NAME:'||NAME||CHR(10)||'LASTNAME:'||
--------------------------------------------------------------------------------
Code of the teacher: JJ000
Name: John
Last name: Jonas
Number of classes given in the session 1000 : 2
Number of classes given in the session 2000 : 3
SQL>
CREATE OR REPLACE PROCEDURE numberOfGiverClasses
(codeOfTeacher Teacher.teacherCode%TYPE) IS
v_teacherCode Teacher.teacherCode%TYPE;
v_name Teacher.name%TYPE;
v_lastName Teacher.lastName%TYPE;
v_sessionCode Class.sessionCode%TYPE;
compteur INTEGER;
CURSOR pro_counter (codeOfTeacher Teacher.teacherCode%TYPE) IS
SELECT sessionCode, COUNT(groupnum)
FROM Class
WHERE teacherCode= codeOfTeacher
GROUP BY sessionCode;
BEGIN
SELECT teacherCode, name, lastName
INTO v_teacherCode, v_name, v_lastName
FROM Teacher
WHERE teacherCode = codeOfTeacher ;
DBMS_OUTPUT.PUT_LINE(rpad('Code of the teacher: ',25) || v_teacherCode);
DBMS_OUTPUT.PUT_LINE(rpad('Name: ',25) || v_name);
DBMS_OUTPUT.PUT_LINE(rpad('Last name: ',25) || v_lastName);
OPEN cursor_q3(codeOfTeacher);
LOOP
FETCH pro_counter INTO v_sessionCode, compteur;
EXIT WHEN pro_counter%NOTFOUND;
DBMS_OUTPUT.PUT('Number of classes given in the session ');
DBMS_OUTPUT.PUT(v_sessionCode);
DBMS_OUTPUT.PUT(': ');
DBMS_OUTPUT.PUT_LINE(compteur);
END LOOP;
CLOSE pro_counter ;
exception when others then
dbms_output.put_line(sqlerrm);
END numberOfGiverClasses;

Referring a nested table in a relational insert query

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.