SlideShare a Scribd company logo
1 of 103
Download to read offline
Design Patterns
in PL/SQL and SQL
Oren Nakdimon
www.db-oriented.com
 oren@db-oriented.com
 +972-54-4393763
@DBoriented
select department_id,
count(*) number_of_employees,
avg(salary) avg_salary
from employees
group by department_id
having max(salary) > 2 * min(salary)
Can you
read this
query easily?
If not, please
sit closer
©
Oren
Nakdimon
©
Oren
Nakdimon
https://db-oriented.com
Who Am
I?
DESIGN PATTERNS
General Definition
©
Oren
Nakdimon
©
Oren
Nakdimon
In software engineering, a software design pattern is a
general, reusable solution to a commonly occurring problem
within a given context in software design. It is not a finished
design that can be transformed directly into source or
machine code. Rather, it is a description or template for how
to solve a problem that can be used in many different
situations. Design patterns are formalized best practices that
the programmer can use to solve common problems when
designing an application or system.
https://en.wikipedia.org/wiki/Software_design_pattern
DESIGN PATTERNS
For Oracle Developers
©
Oren
Nakdimon
©
Oren
Nakdimon
SQL PL/SQL
Data Model
Concrete Task
General Task
Suggested Solution
Challenges
Variations
“Mini-Patterns”
THE DEMO MODEL
©
Oren
Nakdimon
©
Oren
Nakdimon
ALBUM
# id
* title
* release_date
SONG
# track#
* title
o is_album_favourite
ARTIST
# id
* name
* type
* date_of_birth
o date_of_death
LOV_GENRE
# id
* name
OVERLAP CHECK
©
Oren
Nakdimon
©
Oren
Nakdimon
create table artists (
id
integer
generated as identity
not null
constraint artists_pk primary key,
name
varchar2(100)
not null,
type
varchar2(6)
not null
constraint artists_type_chk check (type in ('Person','Group')),
date_of_birth
date
not null
constraint artists_dob_chk check (date_of_birth = trunc(date_of_birth)),
date_of_death
date
constraint artists_dod_chk check (date_of_death = trunc(date_of_death))
);
©
Oren
Nakdimon
©
Oren
Nakdimon
create table artists (
id
integer
generated as identity
not null
constraint artists_pk primary key,
name
varchar2(100)
not null,
type
varchar2(6)
not null
constraint artists_type_chk check (type in ('Person','Group')),
date_of_birth
date
not null
constraint artists_dob_chk check (date_of_birth = trunc(date_of_birth)),
date_of_death
date
constraint artists_dod_chk check (date_of_death = trunc(date_of_death))
);
Mini-Pattern
Implementing a
“pure” date
attribute (with no
time part)
©
Oren
Nakdimon
©
Oren
Nakdimon
create or replace package music is
...
procedure get_people
(
i_from_date in date,
i_to_date in date,
o_artists_rc out sys_refcursor
);
...
end music;
©
Oren
Nakdimon
©
Oren
Nakdimon
Find rows that (partially or fully) overlap a given range
Find all the artists that were alive between 2 dates
Get all the projects that were active in the last 7 days
Get the employees that worked in the company during 2019
©
Oren
Nakdimon
©
Oren
Nakdimon
Disjoint Ranges
©
Oren
Nakdimon
©
Oren
Nakdimon
Disjoint Ranges
Start1 End1
Start2 End2
Start2>End1
©
Oren
Nakdimon
©
Oren
Nakdimon
Disjoint Ranges
Start1 End1
Start2 End2
Start2>End1
or
Start1>End2
©
Oren
Nakdimon
©
Oren
Nakdimon
Overlapping Ranges
Start1 End1
Start2 End2
Start2<End1
and
Start1<End2
©
Oren
Nakdimon
©
Oren
Nakdimon
Overlapping Ranges
Start1 End1
Start2 End2
Start2<End1
and
Start1<End2
©
Oren
Nakdimon
©
Oren
Nakdimon
Overlapping Ranges
Start1 End1
Start2 End2
Start2<End1
and
Start1<End2
©
Oren
Nakdimon
©
Oren
Nakdimon
The Suggested Solution
StartColumn < EndParameter
and
StartParameter < EndColumn
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure get_people
(
i_from_date in date,
i_to_date in date,
o_artists_rc out sys_refcursor
) is
begin
open o_artists_rc for
select ar.name,
ar.date_of_birth,
ar.date_of_death
from artists ar
where ar.type = 'Person'
and ar.date_of_birth < i_to_date
and i_from_date < ar.date_of_death
order by ar.date_of_birth,
ar.name;
end get_people;
@over1
©
Oren
Nakdimon
©
Oren
Nakdimon
Variation: Inclusive vs. Exclusive Ranges
©
Oren
Nakdimon
©
Oren
Nakdimon
The Suggested Solution
StartColumn < EndParameter
and
StartParameter < EndColumn
StartColumn <= EndParameter
and
StartParameter <= EndColumn
Exclusive
Ranges
Inclusive
Ranges
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure get_people
(
i_from_date in date,
i_to_date in date,
o_artists_rc out sys_refcursor
) is
begin
open o_artists_rc for
select ar.name,
ar.date_of_birth,
ar.date_of_death
from artists ar
where ar.type = 'Person'
and ar.date_of_birth <= i_to_date
and i_from_date <= ar.date_of_death
order by ar.date_of_birth,
ar.name;
end get_people;
@over2
©
Oren
Nakdimon
©
Oren
Nakdimon
Variation: Nullable vs. Mandatory Columns
create table artists (
id
integer
generated as identity
not null
constraint artists_pk primary key,
name
varchar2(100)
not null,
type
varchar2(6)
not null
constraint artists_type_chk check (type in ('Person','Band')),
date_of_birth
date
not null
constraint artists_dob_chk check (date_of_birth = trunc(date_of_birth)),
date_of_death
date
constraint artists_dod_chk check (date_of_death = trunc(date_of_death))
);
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure get_people
(
i_from_date in date,
i_to_date in date,
o_artists_rc out sys_refcursor
) is
begin
open o_artists_rc for
select ar.name,
ar.date_of_birth,
ar.date_of_death
from artists ar
where ar.type = 'Person'
and ar.date_of_birth <= i_to_date
and i_from_date <= nvl(ar.date_of_death, date '9999-12-31')
order by ar.date_of_birth,
ar.name;
end get_people;
@over3
EXTENDABLE LOV
©
Oren
Nakdimon
©
Oren
Nakdimon
create table lov_genres (
id
integer
generated as identity
not null
constraint lov_genres_pk primary key,
name
varchar2(20)
not null
);
©
Oren
Nakdimon
©
Oren
Nakdimon
create table albums (
id
integer
generated as identity
not null
constraint albums_pk primary key,
title
varchar2(100)
not null,
release_date
date
not null,
genre_id
constraint albums_fk_genre_id references lov_genres (id)
);
©
Oren
Nakdimon
©
Oren
Nakdimon
create table albums (
id
integer
generated as identity
not null
constraint albums_pk primary key,
title
varchar2(100)
not null,
release_date
date
not null,
genre_id
constraint albums_fk_genre_id references lov_genres (id)
);
Mini-Pattern
The referencing
column implicitly
gets the data type
of the referenced
column
©
Oren
Nakdimon
©
Oren
Nakdimon
create or replace package music is
...
procedure add_album
(
i_title in albums.title%type,
i_release_date in albums.release_date%type,
i_genre_name in lov_genres.name%type default null,
o_album_id out albums.id%type
); ...
end music;
©
Oren
Nakdimon
©
Oren
Nakdimon
create or replace package body music is
...
procedure add_album
(
i_title in albums.title%type,
i_release_date in albums.release_date%type,
i_genre_name in lov_genres.name%type default null,
o_album_id out albums.id%type
) is
l_genre_id lov_genres.id%type;
begin
l_genre_id := get_genre_id(i_genre_name);
insert into albums
(title,
release_date,
genre_id)
values
(i_title,
i_release_date,
l_genre_id)
returning id into o_album_id;
end add_album;
©
Oren
Nakdimon
©
Oren
Nakdimon
Look-up a reference table and add new values if not exist
Get genre id by its name.
If it doesn’t exist, add it.
©
Oren
Nakdimon
©
Oren
Nakdimon
function get_genre_id
(
i_genre_name in lov_genres.name%type,
i_add_if_not_exists in boolean default true
) return lov_genres.id%type
is
l_clean_name lov_genres.name%type :=
initcap(trim(i_genre_name));
l_id lov_genres.id%type;
begin
if i_genre_name is null then
return null;
end if;
begin
select g.id into l_id
from lov_genres g
where g.name = i_genre_name;
exception
when no_data_found then
if i_add_if_not_exists then
begin
insert into lov_genres (name)
values (i_genre_name)
returning id into l_id;
exception
when dup_val_on_index then
l_id := get_genre_id(
i_genre_name => l_clean_name,
i_add_if_not_exists => false);
end;
else
raise_application_error(-20000,
i_genre_name || ' not found');
end if;
end;
return l_id;
end get_genre_id;
©
Oren
Nakdimon
©
Oren
Nakdimon
function get_genre_id
(
i_genre_name in lov_genres.name%type,
i_add_if_not_exists in boolean default true
) return lov_genres.id%type
is
l_clean_name lov_genres.name%type :=
initcap(trim(i_genre_name));
l_id lov_genres.id%type;
begin
if i_genre_name is null then
return null;
end if;
begin
select g.id into l_id
from lov_genres g
where g.name = i_genre_name;
exception
when no_data_found then
if i_add_if_not_exists then
begin
insert into lov_genres (name)
values (i_genre_name)
returning id into l_id;
exception
when dup_val_on_index then
l_id := get_genre_id(
i_genre_name => l_clean_name,
i_add_if_not_exists => false);
end;
else
raise_application_error(-20000,
i_genre_name || ' not found');
end if;
end;
return l_id;
end get_genre_id;
©
Oren
Nakdimon
©
Oren
Nakdimon
function get_genre_id
(
i_genre_name in lov_genres.name%type,
i_add_if_not_exists in boolean default true
) return lov_genres.id%type
is
l_clean_name lov_genres.name%type :=
initcap(trim(i_genre_name));
l_id lov_genres.id%type;
begin
if i_genre_name is null then
return null;
end if;
begin
select g.id into l_id
from lov_genres g
where g.name = i_genre_name;
exception
when no_data_found then
if i_add_if_not_exists then
begin
insert into lov_genres (name)
values (i_genre_name)
returning id into l_id;
exception
when dup_val_on_index then
l_id := get_genre_id(
i_genre_name => l_clean_name,
i_add_if_not_exists => false);
end;
else
raise_application_error(-20000,
i_genre_name || ' not found');
end if;
end;
return l_id;
end get_genre_id;
©
Oren
Nakdimon
©
Oren
Nakdimon
function get_genre_id
(
i_genre_name in lov_genres.name%type,
i_add_if_not_exists in boolean default true
) return lov_genres.id%type
is
l_clean_name lov_genres.name%type :=
initcap(trim(i_genre_name));
l_id lov_genres.id%type;
begin
if i_genre_name is null then
return null;
end if;
begin
select g.id into l_id
from lov_genres g
where g.name = i_genre_name;
exception
when no_data_found then
if i_add_if_not_exists then
begin
insert into lov_genres (name)
values (i_genre_name)
returning id into l_id;
exception
when dup_val_on_index then
l_id := get_genre_id(
i_genre_name => l_clean_name,
i_add_if_not_exists => false);
end;
else
raise_application_error(-20000,
i_genre_name || ' not found');
end if;
end;
return l_id;
end get_genre_id;
©
Oren
Nakdimon
©
Oren
Nakdimon
function get_genre_id
(
i_genre_name in lov_genres.name%type,
i_add_if_not_exists in boolean default true
) return lov_genres.id%type
is
l_clean_name lov_genres.name%type :=
initcap(trim(i_genre_name));
l_id lov_genres.id%type;
begin
if i_genre_name is null then
return null;
end if;
begin
select g.id into l_id
from lov_genres g
where g.name = i_genre_name;
exception
when no_data_found then
if i_add_if_not_exists then
begin
insert into lov_genres (name)
values (i_genre_name)
returning id into l_id;
exception
when dup_val_on_index then
l_id := get_genre_id(
i_genre_name => l_clean_name,
i_add_if_not_exists => false);
end;
else
raise_application_error(-20000,
i_genre_name || ' not found');
end if;
end;
return l_id;
end get_genre_id;
©
Oren
Nakdimon
©
Oren
Nakdimon
function get_genre_id
(
i_genre_name in lov_genres.name%type,
i_add_if_not_exists in boolean default true
) return lov_genres.id%type
is
l_clean_name lov_genres.name%type :=
initcap(trim(i_genre_name));
l_id lov_genres.id%type;
begin
if i_genre_name is null then
return null;
end if;
begin
select g.id into l_id
from lov_genres g
where g.name = i_genre_name;
exception
when no_data_found then
if i_add_if_not_exists then
begin
insert into lov_genres (name)
values (i_genre_name)
returning id into l_id;
exception
when dup_val_on_index then
l_id := get_genre_id(
i_genre_name => l_clean_name,
i_add_if_not_exists => false);
end;
else
raise_application_error(-20000,
i_genre_name || ' not found');
end if;
end;
return l_id;
end get_genre_id;
©
Oren
Nakdimon
©
Oren
Nakdimon
function get_genre_id
(
i_genre_name in lov_genres.name%type,
i_add_if_not_exists in boolean default true
) return lov_genres.id%type
is
l_clean_name lov_genres.name%type :=
initcap(trim(i_genre_name));
l_id lov_genres.id%type;
begin
if i_genre_name is null then
return null;
end if;
begin
select g.id into l_id
from lov_genres g
where g.name = i_genre_name;
exception
when no_data_found then
if i_add_if_not_exists then
begin
insert into lov_genres (name)
values (i_genre_name)
returning id into l_id;
exception
when dup_val_on_index then
l_id := get_genre_id(
i_genre_name => l_clean_name,
i_add_if_not_exists => false);
end;
else
raise_application_error(-20000,
i_genre_name || ' not found');
end if;
end;
return l_id;
end get_genre_id;
©
Oren
Nakdimon
©
Oren
Nakdimon
function get_genre_id
(
i_genre_name in lov_genres.name%type,
i_add_if_not_exists in boolean default true
) return lov_genres.id%type
is
l_clean_name lov_genres.name%type :=
initcap(trim(i_genre_name));
l_id lov_genres.id%type;
begin
if i_genre_name is null then
return null;
end if;
begin
select g.id into l_id
from lov_genres g
where g.name = i_genre_name;
exception
when no_data_found then
if i_add_if_not_exists then
begin
insert into lov_genres (name)
values (i_genre_name)
returning id into l_id;
exception
when dup_val_on_index then
l_id := get_genre_id(
i_genre_name => l_clean_name,
i_add_if_not_exists => false);
end;
else
raise_application_error(-20000,
i_genre_name || ' not found');
end if;
end;
return l_id;
end get_genre_id;
©
Oren
Nakdimon
©
Oren
Nakdimon
function get_genre_id
(
i_genre_name in lov_genres.name%type,
i_add_if_not_exists in boolean default true
) return lov_genres.id%type
is
l_clean_name lov_genres.name%type :=
initcap(trim(i_genre_name));
l_id lov_genres.id%type;
begin
if i_genre_name is null then
return null;
end if;
begin
select g.id into l_id
from lov_genres g
where g.name = i_genre_name;
exception
when no_data_found then
if i_add_if_not_exists then
begin
insert into lov_genres (name)
values (i_genre_name)
returning id into l_id;
exception
when dup_val_on_index then
l_id := get_genre_id(
i_genre_name => l_clean_name,
i_add_if_not_exists => false);
end;
else
raise_application_error(-20000,
i_genre_name || ' not found');
end if;
end;
return l_id;
end get_genre_id;
©
Oren
Nakdimon
©
Oren
Nakdimon
function get_genre_id
(
i_genre_name in lov_genres.name%type,
i_add_if_not_exists in boolean default true
) return lov_genres.id%type
is
l_clean_name lov_genres.name%type :=
initcap(trim(i_genre_name));
l_id lov_genres.id%type;
begin
if i_genre_name is null then
return null;
end if;
begin
select g.id into l_id
from lov_genres g
where g.name = i_genre_name;
exception
when no_data_found then
if i_add_if_not_exists then
begin
insert into lov_genres (name)
values (i_genre_name)
returning id into l_id;
exception
when dup_val_on_index then
l_id := get_genre_id(
i_genre_name => l_clean_name,
i_add_if_not_exists => false);
end;
else
raise_application_error(-20000,
i_genre_name || ' not found');
end if;
end;
return l_id;
end get_genre_id;
@lov1
©
Oren
Nakdimon
©
Oren
Nakdimon
Look-up a reference table and add new values if not exist
Get genre id by its name.
If it doesn’t exist, add it.
➢Name Standardization
©
Oren
Nakdimon
©
Oren
Nakdimon
create table lov_genres (
id
integer
generated as identity
not null
constraint lov_genres_pk primary key,
name
varchar2(20)
not null
);
alter table lov_genres add
constraint lov_genres_name_chk check (name = initcap(trim(name)));
©
Oren
Nakdimon
©
Oren
Nakdimon
function get_genre_id
(
i_genre_name in lov_genres.name%type,
i_add_if_not_exists in boolean default true
) return lov_genres.id%type
is
l_clean_name lov_genres.name%type :=
initcap(trim(i_genre_name));
l_id lov_genres.id%type;
begin
if i_genre_name is null then
return null;
end if;
begin
select g.id into l_id
from lov_genres g
where g.name = l_clean_name;
exception
when no_data_found then
if i_add_if_not_exists then
begin
insert into lov_genres (name)
values (l_clean_name)
returning id into l_id;
exception
when dup_val_on_index then
l_id := get_genre_id(
i_genre_name => l_clean_name,
i_add_if_not_exists => false);
end;
else
raise_application_error(-20000,
i_genre_name || ' not found');
end if;
end;
return l_id;
end get_genre_id;
@lov2
©
Oren
Nakdimon
©
Oren
Nakdimon
Look-up a reference table and add new values if not exist
Get genre id by its name.
If it doesn’t exist, add it.
➢Name Standardization
➢Uniqueness
©
Oren
Nakdimon
©
Oren
Nakdimon
create table lov_genres (
id
integer
generated as identity
not null
constraint lov_genres_pk primary key,
name
varchar2(20)
not null
);
alter table lov_genres add
constraint lov_genres_name_chk check (name = initcap(trim(name)));
alter table lov_genres add
constraint lov_genres_name_uk unique (name);
@lov3
©
Oren
Nakdimon
©
Oren
Nakdimon
Look-up a reference table and add new values if not exist
Get genre id by its name.
If it doesn’t exist, add it.
➢Name Standardization
➢Uniqueness
➢Concurrency Control
©
Oren
Nakdimon
©
Oren
Nakdimon
function get_genre_id
(
i_genre_name in lov_genres.name%type,
i_add_if_not_exists in boolean default true
) return lov_genres.id%type
is
l_clean_name lov_genres.name%type :=
initcap(trim(i_genre_name));
l_id lov_genres.id%type;
begin
if i_genre_name is null then
return null;
end if;
begin
select g.id into l_id
from lov_genres g
where g.name = l_clean_name;
exception
when no_data_found then
if i_add_if_not_exists then
begin
insert into lov_genres (name)
values (l_clean_name)
returning id into l_id;
exception
when dup_val_on_index then
l_id := get_genre_id(
i_genre_name => l_clean_name,
i_add_if_not_exists => false);
end;
else
raise_application_error(-20000,
i_genre_name || ' not found');
end if;
end;
return l_id;
end get_genre_id;
@lov4
MULTI-VALUE PARAMETERS
©
Oren
Nakdimon
©
Oren
Nakdimon
create table songs (
album_id
not null
constraint songs_fk_album_id references albums
on delete cascade,
track#
number(2)
not null,
constraint songs_pk primary key (album_id,track#),
title
varchar2(100)
not null,
artist_id
not null
constraint songs_fk_atrist_id references artists,
is_album_favourite
number(1)
default 0
not null
constraint songs_favourite_boolean_chk check (is_album_favourite in (0,1))
);
create index songs_artist_id_ix on songs (artist_id) compress;
©
Oren
Nakdimon
©
Oren
Nakdimon
create table songs (
album_id
not null
constraint songs_fk_album_id references albums
on delete cascade,
track#
number(2)
not null,
constraint songs_pk primary key (album_id,track#),
title
varchar2(100)
not null,
artist_id
not null
constraint songs_fk_atrist_id references artists,
is_album_favourite
number(1)
default 0
not null
constraint songs_favourite_boolean_chk check (is_album_favourite in (0,1))
);
create index songs_artist_id_ix on songs (artist_id) compress;
Mini-Pattern
Implementing
a Boolean
attribute
©
Oren
Nakdimon
©
Oren
Nakdimon
create table songs (
album_id
not null
constraint songs_fk_album_id references albums
on delete cascade,
track#
number(2)
not null,
constraint songs_pk primary key (album_id,track#),
title
varchar2(100)
not null,
artist_id
not null
constraint songs_fk_atrist_id references artists,
is_album_favourite
number(1)
default 0
not null
constraint songs_favourite_boolean_chk check (is_album_favourite in (0,1))
);
create index songs_artist_id_ix on songs (artist_id) compress;
Mini-Pattern
Implementing
a weak entity
©
Oren
Nakdimon
©
Oren
Nakdimon
Pass multiple entities to procedures
Add an album with its songs
➢Related Entities
©
Oren
Nakdimon
©
Oren
Nakdimon
create or replace package music is
...
procedure add_album
(
i_title in albums.title%type,
i_release_date in albums.release_date%type,
i_genre_name in lov_genres.name%type default null,
i_songs in ???,
o_album_id out albums.id%type
);
...
end music;
©
Oren
Nakdimon
©
Oren
Nakdimon
create type song_t as object (
track# number(2),
title varchar2(100),
artist_id integer
)
/
©
Oren
Nakdimon
©
Oren
Nakdimon
create type song_t as object (
track# number(2),
title varchar2(100),
artist_id integer
)
/
create type song_tt as table of song_t
/
©
Oren
Nakdimon
©
Oren
Nakdimon
create type song_t as object (
track# number(2),
title varchar2(100),
artist_id integer
)
/
create type song_tt as table of song_t
/
create or replace package music is
...
procedure add_album
(
i_title in albums.title%type,
i_release_date in albums.release_date%type,
i_genre_name in lov_genres.name%type default null,
i_songs in song_tt,
o_album_id out albums.id%type
);
...
end music;
©
Oren
Nakdimon
©
Oren
Nakdimon
Pass multiple entities to procedures
Add an album with its songs
➢Related Entities
➢Performance – less roundtrips
©
Oren
Nakdimon
©
Oren
Nakdimon
create or replace package body music is
...
procedure add_album
(
i_title in albums.title%type,
i_release_date in albums.release_date%type,
i_genre_name in lov_genres.name%type default null,
o_album_id out albums.id%type
) is
l_genre_id lov_genres.id%type;
begin
l_genre_id := get_genre_id(i_genre_name);
insert into albums
(title,
release_date,
genre_id)
values
(i_title,
i_release_date,
l_genre_id)
returning id into o_album_id;
end add_album;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure add_album
(
i_title in albums.title%type,
i_release_date in albums.release_date%type,
i_genre_name in lov_genres.name%type default null,
i_songs in song_tt,
o_album_id out albums.id%type
) is
l_genre_id lov_genres.id%type;
begin
l_genre_id := get_genre_id(i_genre_name);
insert into albums
(title,
release_date,
genre_id)
values
(i_title,
i_release_date,
l_genre_id)
returning id into o_album_id;
forall i in indices of i_songs
insert into songs
(album_id,
track#,
title,
artist_id)
values
(o_album_id,
i_songs(i).track#,
i_songs(i).title,
i_songs(i).artist_id);
end add_album;
©
Oren
Nakdimon
©
Oren
Nakdimon
Pass multiple entities to procedures
Add an album with its songs
➢Related Entities
➢Performance – less roundtrips
➢Performance – less context switches
➢Atomicity
@coll1
THE GUARDIAN TRIGGER
©
Oren
Nakdimon
©
Oren
Nakdimon
Enforce the rule: an album must have at least one song
ALBUM
SONG
©
Oren
Nakdimon
©
Oren
Nakdimon
Enforce the rule: an album must have at least one song
ALBUM
SONG
©
Oren
Nakdimon
©
Oren
Nakdimon
Enforce the rule: an album must have at least one song
ALBUM
SONG
©
Oren
Nakdimon
©
Oren
Nakdimon
Enforce the rule: an album must have at least one song
ALBUM
SONG
Whenever some DML is done on some table T,
another piece of code should be executed
Complex Validity Check
Logging
Maintaining Denormalized Entities
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure add_album (
i_title in albums.title%type,
i_release_date in albums.release_date%type,
i_genre_name in lov_genres.name%type default null,
i_songs in song_tt,
o_album_id out albums.id%type)
is
l_genre_id lov_genres.id%type;
begin
if i_songs is null or i_songs is empty then
raise_application_error(
-20000, 'an album must have songs');
end if;
l_genre_id := get_genre_id(i_genre_name);
set_album_songs_integrity_enforced(true);
insert into albums (title, release_date, genre_id)
values (i_title, i_release_date, l_genre_id)
returning id into o_album_id;
forall i in indices of i_songs
insert into songs
(album_id,
track#,
title,
artist_id)
values
(o_album_id,
i_songs(i).track#,
i_songs(i).title,
i_songs(i).artist_id);
set_album_songs_integrity_enforced
(false);
exception
when others then
set_album_songs_integrity_enforced
(false);
raise;
end add_album;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure add_album (
i_title in albums.title%type,
i_release_date in albums.release_date%type,
i_genre_name in lov_genres.name%type default null,
i_songs in song_tt,
o_album_id out albums.id%type)
is
l_genre_id lov_genres.id%type;
begin
if i_songs is null or i_songs is empty then
raise_application_error(
-20000, 'an album must have songs');
end if;
l_genre_id := get_genre_id(i_genre_name);
set_album_songs_integrity_enforced(true);
insert into albums (title, release_date, genre_id)
values (i_title, i_release_date, l_genre_id)
returning id into o_album_id;
forall i in indices of i_songs
insert into songs
(album_id,
track#,
title,
artist_id)
values
(o_album_id,
i_songs(i).track#,
i_songs(i).title,
i_songs(i).artist_id);
set_album_songs_integrity_enforced
(false);
exception
when others then
set_album_songs_integrity_enforced
(false);
raise;
end add_album;
create or replace package body music is
g_album_songs_integrity_enforced boolean not null := false;
function is_album_songs_integrity_enforced return boolean is
begin
return g_album_songs_integrity_enforced;
end is_album_songs_integrity_enforced;
procedure set_album_songs_integrity_enforced(i_is_enforced in boolean) is
begin
g_album_songs_integrity_enforced := i_is_enforced;
end set_album_songs_integrity_enforced;
...
end music;
©
Oren
Nakdimon
©
Oren
Nakdimon
create or replace package body music is
g_album_songs_integrity_enforced boolean not null := false;
function is_album_songs_integrity_enforced return boolean is
begin
return g_album_songs_integrity_enforced;
end is_album_songs_integrity_enforced;
procedure set_album_songs_integrity_enforced(i_is_enforced in boolean) is
begin
g_album_songs_integrity_enforced := i_is_enforced;
end set_album_songs_integrity_enforced;
...
end music;
©
Oren
Nakdimon
©
Oren
Nakdimon
create or replace package body music is
g_album_songs_integrity_enforced boolean not null := false;
function is_album_songs_integrity_enforced return boolean is
begin
return g_album_songs_integrity_enforced;
end is_album_songs_integrity_enforced;
procedure set_album_songs_integrity_enforced(i_is_enforced in boolean) is
begin
g_album_songs_integrity_enforced := i_is_enforced;
end set_album_songs_integrity_enforced;
...
end music;
©
Oren
Nakdimon
©
Oren
Nakdimon
create or replace package body music is
g_album_songs_integrity_enforced boolean not null := false;
function is_album_songs_integrity_enforced return boolean is
begin
return g_album_songs_integrity_enforced;
end is_album_songs_integrity_enforced;
procedure set_album_songs_integrity_enforced(i_is_enforced in boolean) is
begin
g_album_songs_integrity_enforced := i_is_enforced;
end set_album_songs_integrity_enforced;
...
end music;
©
Oren
Nakdimon
©
Oren
Nakdimon
create or replace package body music is
g_album_songs_integrity_enforced boolean not null := false;
function is_album_songs_integrity_enforced return boolean is
begin
return g_album_songs_integrity_enforced;
end is_album_songs_integrity_enforced;
procedure set_album_songs_integrity_enforced(i_is_enforced in boolean) is
begin
g_album_songs_integrity_enforced := i_is_enforced;
end set_album_songs_integrity_enforced;
...
end music;
create or replace package music is
function is_album_songs_integrity_enforced return Boolean;
...
end music;
©
Oren
Nakdimon
©
Oren
Nakdimon
create or replace trigger albums_guardian_trigger
before insert on albums
begin
if not music.is_album_songs_integrity_enforced then
raise_application_error(
-20000,
'to maintain albums and songs, please use the procedures in the music package');
end if;
end albums_guardian_trigger;
/
create or replace trigger songs_guardian_trigger
before delete or update of album_id on songs
begin
if not music.is_album_songs_integrity_enforced then
raise_application_error(
-20000,
'to maintain albums and songs, please use the procedures in the music package');
end if;
end songs_guardian_trigger;
/
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure add_album (
i_title in albums.title%type,
i_release_date in albums.release_date%type,
i_genre_name in lov_genres.name%type default null,
i_songs in song_tt,
o_album_id out albums.id%type)
is
l_genre_id lov_genres.id%type;
begin
if i_songs is null or i_songs is empty then
raise_application_error(
-20000, 'an album must have songs');
end if;
l_genre_id := get_genre_id(i_genre_name);
set_album_songs_integrity_enforced(true);
insert into albums (title, release_date, genre_id)
values (i_title, i_release_date, l_genre_id)
returning id into o_album_id;
forall i in indices of i_songs
insert into songs
(album_id,
track#,
title,
artist_id)
values
(o_album_id,
i_songs(i).track#,
i_songs(i).title,
i_songs(i).artist_id);
set_album_songs_integrity_enforced
(false);
exception
when others then
set_album_songs_integrity_enforced
(false);
raise;
end add_album;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure add_album (
i_title in albums.title%type,
i_release_date in albums.release_date%type,
i_genre_name in lov_genres.name%type default null,
i_songs in song_tt,
o_album_id out albums.id%type)
is
l_genre_id lov_genres.id%type;
begin
if i_songs is null or i_songs is empty then
raise_application_error(
-20000, 'an album must have songs');
end if;
l_genre_id := get_genre_id(i_genre_name);
set_album_songs_integrity_enforced(true);
insert into albums (title, release_date, genre_id)
values (i_title, i_release_date, l_genre_id)
returning id into o_album_id;
forall i in indices of i_songs
insert into songs
(album_id,
track#,
title,
artist_id)
values
(o_album_id,
i_songs(i).track#,
i_songs(i).title,
i_songs(i).artist_id);
set_album_songs_integrity_enforced
(false);
exception
when others then
set_album_songs_integrity_enforced
(false);
raise;
end add_album;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure add_album (
i_title in albums.title%type,
i_release_date in albums.release_date%type,
i_genre_name in lov_genres.name%type default null,
i_songs in song_tt,
o_album_id out albums.id%type)
is
l_genre_id lov_genres.id%type;
begin
if i_songs is null or i_songs is empty then
raise_application_error(
-20000, 'an album must have songs');
end if;
l_genre_id := get_genre_id(i_genre_name);
set_album_songs_integrity_enforced(true);
insert into albums (title, release_date, genre_id)
values (i_title, i_release_date, l_genre_id)
returning id into o_album_id;
forall i in indices of i_songs
insert into songs
(album_id,
track#,
title,
artist_id)
values
(o_album_id,
i_songs(i).track#,
i_songs(i).title,
i_songs(i).artist_id);
set_album_songs_integrity_enforced
(false);
exception
when others then
set_album_songs_integrity_enforced
(false);
raise;
end add_album;
@trig
©
Oren
Nakdimon
©
Oren
Nakdimon
The Suggested Solution
 Put the logic in a procedure (or procedures)
 Use a global variable that indicates if the DML is allowed
 Initialize it to FALSE
 Set it to TRUE only for the duration of the procedure
 A statement-level trigger on the DML verifies the flag is on
ARC RELATIONSHIP
©
Oren
Nakdimon
©
Oren
Nakdimon
ALBUM
# id
* title
* release_date
SONG
# track#
* title
o is_album_favourite
ARTIST
# id
* name
* type
* date_of_birth
o date_of_death
LOV_GENRE
# id
* name
IMAGE
# id
* name
* size
* resolution
©
Oren
Nakdimon
©
Oren
Nakdimon
Implement an arc relationship from Images to Artists and Albums
Implement an arc relationship from a generic entity
©
Oren
Nakdimon
©
Oren
Nakdimon
Option #1 – Multiple Dedicated Tables
ALBUM_IMAGES
# image_id
* album_id (FK)
* name
* size
* resolution
.
.
.
ARTIST_IMAGES
# image_id
* artist_id (FK)
* name
* size
* resolution
.
.
.
?_IMAGES
# image_id
* ?_id (FK)
* name
* size
* resolution
.
.
.
Code Code Code
©
Oren
Nakdimon
©
Oren
Nakdimon
Option #2 – A Single Table with Dedicated Columns
IMAGES
# image_id
o album_id (FK)
o artist_id (FK)
* name
* size
* resolution
.
.
.
CHECK
(album_id is null and artist_id is not null
or
album_id is not null and artist_id is null)
©
Oren
Nakdimon
©
Oren
Nakdimon
CHECK
(nvl2(album_id,1,0)+nvl2(artist_id,1,0)=1)
Option #2 – A Single Table with Dedicated Columns
IMAGES
# image_id
o album_id (FK)
o artist_id (FK)
* name
* size
* resolution
.
.
.
Code
album/artist
©
Oren
Nakdimon
©
Oren
Nakdimon
Option #3 – A Generic Table
IMAGES
# image_id
o owner_type
o owner_id
* name
* size
* resolution
.
.
.
Code
check (owner_type in ('Artist','Album'))
No Referential Integrity
©
Oren
Nakdimon
©
Oren
Nakdimon
The Suggested Solution – Option #4
 The generic table solution (option #3)
 + virtual columns to enforce referential integrity
©
Oren
Nakdimon
©
Oren
Nakdimon
create table images (
id
integer generated as identity not null constraint images_pk primary key,
name
varchar2(200) not null,
size_in_bytes
integer not null,
resolution
integer not null,
--- ...
owner_type
varchar2(6) not null
constraint images_owner_type_chk check (owner_type in ('Artist','Album')),
owner_id
integer not null,
artist_id
invisible
generated always as (decode(owner_type,'Artist',owner_id)) virtual
constraint images_fk_artist_id references artists(id) on delete cascade,
album_id
invisible
generated always as (decode(owner_type,'Album',owner_id)) virtual
constraint images_fk_album_id references albums(id) on delete cascade
);
©
Oren
Nakdimon
©
Oren
Nakdimon
create table images (
id
integer generated as identity not null constraint images_pk primary key,
name
varchar2(200) not null,
size_in_bytes
integer not null,
resolution
integer not null,
--- ...
owner_type
varchar2(6) not null
constraint images_owner_type_chk check (owner_type in ('Artist','Album')),
owner_id
integer not null,
artist_id
invisible
generated always as (decode(owner_type,'Artist',owner_id)) virtual
constraint images_fk_artist_id references artists(id) on delete cascade,
album_id
invisible
generated always as (decode(owner_type,'Album',owner_id)) virtual
constraint images_fk_album_id references albums(id) on delete cascade
);
©
Oren
Nakdimon
©
Oren
Nakdimon
create table images (
id
integer generated as identity not null constraint images_pk primary key,
name
varchar2(200) not null,
size_in_bytes
integer not null,
resolution
integer not null,
--- ...
owner_type
varchar2(6) not null
constraint images_owner_type_chk check (owner_type in ('Artist','Album')),
owner_id
integer not null,
artist_id
invisible
generated always as (decode(owner_type,'Artist',owner_id)) virtual
constraint images_fk_artist_id references artists(id) on delete cascade,
album_id
invisible
generated always as (decode(owner_type,'Album',owner_id)) virtual
constraint images_fk_album_id references albums(id) on delete cascade
);
©
Oren
Nakdimon
©
Oren
Nakdimon
create table images (
id
integer generated as identity not null constraint images_pk primary key,
name
varchar2(200) not null,
size_in_bytes
integer not null,
resolution
integer not null,
--- ...
owner_type
varchar2(6) not null
constraint images_owner_type_chk check (owner_type in ('Artist','Album')),
owner_id
integer not null,
artist_id
invisible
generated always as (decode(owner_type,'Artist',owner_id)) virtual
constraint images_fk_artist_id references artists(id) on delete cascade,
album_id
invisible
generated always as (decode(owner_type,'Album',owner_id)) virtual
constraint images_fk_album_id references albums(id) on delete cascade
);
©
Oren
Nakdimon
©
Oren
Nakdimon
create table images (
id
integer generated as identity not null constraint images_pk primary key,
name
varchar2(200) not null,
size_in_bytes
integer not null,
resolution
integer not null,
--- ...
owner_type
varchar2(6) not null
constraint images_owner_type_chk check (owner_type in ('Artist','Album')),
owner_id
integer not null,
artist_id
invisible
generated always as (decode(owner_type,'Artist',owner_id)) virtual
constraint images_fk_artist_id references artists(id) on delete cascade,
album_id
invisible
generated always as (decode(owner_type,'Album',owner_id)) virtual
constraint images_fk_album_id references albums(id) on delete cascade
);
©
Oren
Nakdimon
©
Oren
Nakdimon
create table images (
id
integer generated as identity not null constraint images_pk primary key,
name
varchar2(200) not null,
size_in_bytes
integer not null,
resolution
integer not null,
--- ...
owner_type
varchar2(6) not null
constraint images_owner_type_chk check (owner_type in ('Artist','Album')),
owner_id
integer not null,
artist_id
invisible
generated always as (decode(owner_type,'Artist',owner_id)) virtual
constraint images_fk_artist_id references artists(id) on delete cascade,
album_id
invisible
generated always as (decode(owner_type,'Album',owner_id)) virtual
constraint images_fk_album_id references albums(id) on delete cascade
);
©
Oren
Nakdimon
©
Oren
Nakdimon
create table images (
id
integer generated as identity not null constraint images_pk primary key,
name
varchar2(200) not null,
size_in_bytes
integer not null,
resolution
integer not null,
--- ...
owner_type
varchar2(6) not null
constraint images_owner_type_chk check (owner_type in ('Artist','Album')),
owner_id
integer not null,
artist_id
invisible
generated always as (decode(owner_type,'Artist',owner_id)) virtual
constraint images_fk_artist_id references artists(id) on delete cascade,
album_id
invisible
generated always as (decode(owner_type,'Album',owner_id)) virtual
constraint images_fk_album_id references albums(id) on delete cascade
);
©
Oren
Nakdimon
©
Oren
Nakdimon
create or replace package images_dl is
procedure add_image
(
i_owner_type in images.owner_type%type,
i_owner_id in images.owner_id%type,
i_name in images.name%type,
i_size in images.size_in_bytes%type,
i_resolution in images.resolution%type,
o_id out images.id%type
);
procedure delete_image(i_id in images.id%type);
procedure get_images
(
i_owner_type in images.owner_type%type,
i_owner_id in images.owner_id%type,
o_images_rc out sys_refcursor
);
end images_dl;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure add_image
(
i_owner_type in images.owner_type%type,
i_owner_id in images.owner_id%type,
i_name in images.name%type,
i_size in images.size_in_bytes%type,
i_resolution in images.resolution%type,
o_id out images.id%type
) is
begin
insert into images
(name,
size_in_bytes,
resolution,
owner_type,
owner_id)
values
(i_name,
i_size,
i_resolution,
i_owner_type,
i_owner_id)
returning id into o_id;
end add_image;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure delete_image(i_id in images.id%type) is
begin
delete images i
where i.id = i_id;
end delete_image;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure get_images
(
i_owner_type in images.owner_type%type,
i_owner_id in images.owner_id%type,
o_images_rc out sys_refcursor
) is
begin
open o_images_rc for
select i.owner_id,
i.id,
i.name,
i.size_in_bytes,
i.resolution
from images i
where i.owner_type = i_owner_type
and i.owner_id = i_owner_id
order by i.id;
end get_images;
©
Oren
Nakdimon
©
Oren
Nakdimon
create or replace package music is
...
procedure get_artist
(
i_id in artists.id%type,
o_name out artists.name%type,
o_type out artists.type%type,
o_date_of_birth out artists.date_of_birth%type,
o_date_of_death out artists.date_of_death%type,
o_images_rc out sys_refcursor
);
procedure get_album
(
i_id in albums.id%type,
o_title out albums.title%type,
o_songs_rc out sys_refcursor,
o_images_rc out sys_refcursor
);
...
end music;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure get_artist
(
i_id in artists.id%type,
o_name out artists.name%type,
o_type out artists.type%type,
o_date_of_birth out artists.date_of_birth%type,
o_date_of_death out artists.date_of_death%type,
o_images_rc out sys_refcursor
) is
begin
select ar.name,
ar.type,
ar.date_of_birth,
ar.date_of_death
into o_name,
o_type,
o_date_of_birth,
o_date_of_death
from artists ar
where ar.id = i_id;
images_dl.get_images(i_owner_type => 'Artist',
i_owner_id => i_id,
o_images_rc => o_images_rc);
end get_artist;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure get_album
(
i_id in albums.id%type,
o_title out albums.title%type,
o_songs_rc out sys_refcursor,
o_images_rc out sys_refcursor
) is
begin
select al.title into o_title
from albums al
where al.id = i_id;
open o_songs_rc for
select s.album_id, s.track#, s.title, s.artist_id, s.is_album_favourite
from songs s
where s.album_id = i_id
order by s.track#;
images_dl.get_images(i_owner_type => 'Album',
i_owner_id => i_id,
o_images_rc => o_images_rc);
end get_album;
THANK YOU☺
Oren Nakdimon
www.db-oriented.com
 oren@db-oriented.com
 +972-54-4393763
@DBoriented

More Related Content

Recently uploaded

08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsRoshan Dwivedi
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 

Recently uploaded (20)

08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 

Featured

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by HubspotMarius Sescu
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTExpeed Software
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsPixeldarts
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthThinkNow
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 

Featured (20)

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 

Design Patterns in PLSQL and SQL [UKOUG 22]

  • 1. Design Patterns in PL/SQL and SQL Oren Nakdimon www.db-oriented.com  oren@db-oriented.com  +972-54-4393763 @DBoriented select department_id, count(*) number_of_employees, avg(salary) avg_salary from employees group by department_id having max(salary) > 2 * min(salary) Can you read this query easily? If not, please sit closer
  • 4. © Oren Nakdimon © Oren Nakdimon In software engineering, a software design pattern is a general, reusable solution to a commonly occurring problem within a given context in software design. It is not a finished design that can be transformed directly into source or machine code. Rather, it is a description or template for how to solve a problem that can be used in many different situations. Design patterns are formalized best practices that the programmer can use to solve common problems when designing an application or system. https://en.wikipedia.org/wiki/Software_design_pattern
  • 6. © Oren Nakdimon © Oren Nakdimon SQL PL/SQL Data Model Concrete Task General Task Suggested Solution Challenges Variations “Mini-Patterns”
  • 8. © Oren Nakdimon © Oren Nakdimon ALBUM # id * title * release_date SONG # track# * title o is_album_favourite ARTIST # id * name * type * date_of_birth o date_of_death LOV_GENRE # id * name
  • 10. © Oren Nakdimon © Oren Nakdimon create table artists ( id integer generated as identity not null constraint artists_pk primary key, name varchar2(100) not null, type varchar2(6) not null constraint artists_type_chk check (type in ('Person','Group')), date_of_birth date not null constraint artists_dob_chk check (date_of_birth = trunc(date_of_birth)), date_of_death date constraint artists_dod_chk check (date_of_death = trunc(date_of_death)) );
  • 11. © Oren Nakdimon © Oren Nakdimon create table artists ( id integer generated as identity not null constraint artists_pk primary key, name varchar2(100) not null, type varchar2(6) not null constraint artists_type_chk check (type in ('Person','Group')), date_of_birth date not null constraint artists_dob_chk check (date_of_birth = trunc(date_of_birth)), date_of_death date constraint artists_dod_chk check (date_of_death = trunc(date_of_death)) ); Mini-Pattern Implementing a “pure” date attribute (with no time part)
  • 12. © Oren Nakdimon © Oren Nakdimon create or replace package music is ... procedure get_people ( i_from_date in date, i_to_date in date, o_artists_rc out sys_refcursor ); ... end music;
  • 13. © Oren Nakdimon © Oren Nakdimon Find rows that (partially or fully) overlap a given range Find all the artists that were alive between 2 dates Get all the projects that were active in the last 7 days Get the employees that worked in the company during 2019
  • 20. © Oren Nakdimon © Oren Nakdimon The Suggested Solution StartColumn < EndParameter and StartParameter < EndColumn
  • 21. © Oren Nakdimon © Oren Nakdimon procedure get_people ( i_from_date in date, i_to_date in date, o_artists_rc out sys_refcursor ) is begin open o_artists_rc for select ar.name, ar.date_of_birth, ar.date_of_death from artists ar where ar.type = 'Person' and ar.date_of_birth < i_to_date and i_from_date < ar.date_of_death order by ar.date_of_birth, ar.name; end get_people; @over1
  • 23. © Oren Nakdimon © Oren Nakdimon The Suggested Solution StartColumn < EndParameter and StartParameter < EndColumn StartColumn <= EndParameter and StartParameter <= EndColumn Exclusive Ranges Inclusive Ranges
  • 24. © Oren Nakdimon © Oren Nakdimon procedure get_people ( i_from_date in date, i_to_date in date, o_artists_rc out sys_refcursor ) is begin open o_artists_rc for select ar.name, ar.date_of_birth, ar.date_of_death from artists ar where ar.type = 'Person' and ar.date_of_birth <= i_to_date and i_from_date <= ar.date_of_death order by ar.date_of_birth, ar.name; end get_people; @over2
  • 25. © Oren Nakdimon © Oren Nakdimon Variation: Nullable vs. Mandatory Columns create table artists ( id integer generated as identity not null constraint artists_pk primary key, name varchar2(100) not null, type varchar2(6) not null constraint artists_type_chk check (type in ('Person','Band')), date_of_birth date not null constraint artists_dob_chk check (date_of_birth = trunc(date_of_birth)), date_of_death date constraint artists_dod_chk check (date_of_death = trunc(date_of_death)) );
  • 26. © Oren Nakdimon © Oren Nakdimon procedure get_people ( i_from_date in date, i_to_date in date, o_artists_rc out sys_refcursor ) is begin open o_artists_rc for select ar.name, ar.date_of_birth, ar.date_of_death from artists ar where ar.type = 'Person' and ar.date_of_birth <= i_to_date and i_from_date <= nvl(ar.date_of_death, date '9999-12-31') order by ar.date_of_birth, ar.name; end get_people; @over3
  • 28. © Oren Nakdimon © Oren Nakdimon create table lov_genres ( id integer generated as identity not null constraint lov_genres_pk primary key, name varchar2(20) not null );
  • 29. © Oren Nakdimon © Oren Nakdimon create table albums ( id integer generated as identity not null constraint albums_pk primary key, title varchar2(100) not null, release_date date not null, genre_id constraint albums_fk_genre_id references lov_genres (id) );
  • 30. © Oren Nakdimon © Oren Nakdimon create table albums ( id integer generated as identity not null constraint albums_pk primary key, title varchar2(100) not null, release_date date not null, genre_id constraint albums_fk_genre_id references lov_genres (id) ); Mini-Pattern The referencing column implicitly gets the data type of the referenced column
  • 31. © Oren Nakdimon © Oren Nakdimon create or replace package music is ... procedure add_album ( i_title in albums.title%type, i_release_date in albums.release_date%type, i_genre_name in lov_genres.name%type default null, o_album_id out albums.id%type ); ... end music;
  • 32. © Oren Nakdimon © Oren Nakdimon create or replace package body music is ... procedure add_album ( i_title in albums.title%type, i_release_date in albums.release_date%type, i_genre_name in lov_genres.name%type default null, o_album_id out albums.id%type ) is l_genre_id lov_genres.id%type; begin l_genre_id := get_genre_id(i_genre_name); insert into albums (title, release_date, genre_id) values (i_title, i_release_date, l_genre_id) returning id into o_album_id; end add_album;
  • 33. © Oren Nakdimon © Oren Nakdimon Look-up a reference table and add new values if not exist Get genre id by its name. If it doesn’t exist, add it.
  • 34. © Oren Nakdimon © Oren Nakdimon function get_genre_id ( i_genre_name in lov_genres.name%type, i_add_if_not_exists in boolean default true ) return lov_genres.id%type is l_clean_name lov_genres.name%type := initcap(trim(i_genre_name)); l_id lov_genres.id%type; begin if i_genre_name is null then return null; end if; begin select g.id into l_id from lov_genres g where g.name = i_genre_name; exception when no_data_found then if i_add_if_not_exists then begin insert into lov_genres (name) values (i_genre_name) returning id into l_id; exception when dup_val_on_index then l_id := get_genre_id( i_genre_name => l_clean_name, i_add_if_not_exists => false); end; else raise_application_error(-20000, i_genre_name || ' not found'); end if; end; return l_id; end get_genre_id;
  • 35. © Oren Nakdimon © Oren Nakdimon function get_genre_id ( i_genre_name in lov_genres.name%type, i_add_if_not_exists in boolean default true ) return lov_genres.id%type is l_clean_name lov_genres.name%type := initcap(trim(i_genre_name)); l_id lov_genres.id%type; begin if i_genre_name is null then return null; end if; begin select g.id into l_id from lov_genres g where g.name = i_genre_name; exception when no_data_found then if i_add_if_not_exists then begin insert into lov_genres (name) values (i_genre_name) returning id into l_id; exception when dup_val_on_index then l_id := get_genre_id( i_genre_name => l_clean_name, i_add_if_not_exists => false); end; else raise_application_error(-20000, i_genre_name || ' not found'); end if; end; return l_id; end get_genre_id;
  • 36. © Oren Nakdimon © Oren Nakdimon function get_genre_id ( i_genre_name in lov_genres.name%type, i_add_if_not_exists in boolean default true ) return lov_genres.id%type is l_clean_name lov_genres.name%type := initcap(trim(i_genre_name)); l_id lov_genres.id%type; begin if i_genre_name is null then return null; end if; begin select g.id into l_id from lov_genres g where g.name = i_genre_name; exception when no_data_found then if i_add_if_not_exists then begin insert into lov_genres (name) values (i_genre_name) returning id into l_id; exception when dup_val_on_index then l_id := get_genre_id( i_genre_name => l_clean_name, i_add_if_not_exists => false); end; else raise_application_error(-20000, i_genre_name || ' not found'); end if; end; return l_id; end get_genre_id;
  • 37. © Oren Nakdimon © Oren Nakdimon function get_genre_id ( i_genre_name in lov_genres.name%type, i_add_if_not_exists in boolean default true ) return lov_genres.id%type is l_clean_name lov_genres.name%type := initcap(trim(i_genre_name)); l_id lov_genres.id%type; begin if i_genre_name is null then return null; end if; begin select g.id into l_id from lov_genres g where g.name = i_genre_name; exception when no_data_found then if i_add_if_not_exists then begin insert into lov_genres (name) values (i_genre_name) returning id into l_id; exception when dup_val_on_index then l_id := get_genre_id( i_genre_name => l_clean_name, i_add_if_not_exists => false); end; else raise_application_error(-20000, i_genre_name || ' not found'); end if; end; return l_id; end get_genre_id;
  • 38. © Oren Nakdimon © Oren Nakdimon function get_genre_id ( i_genre_name in lov_genres.name%type, i_add_if_not_exists in boolean default true ) return lov_genres.id%type is l_clean_name lov_genres.name%type := initcap(trim(i_genre_name)); l_id lov_genres.id%type; begin if i_genre_name is null then return null; end if; begin select g.id into l_id from lov_genres g where g.name = i_genre_name; exception when no_data_found then if i_add_if_not_exists then begin insert into lov_genres (name) values (i_genre_name) returning id into l_id; exception when dup_val_on_index then l_id := get_genre_id( i_genre_name => l_clean_name, i_add_if_not_exists => false); end; else raise_application_error(-20000, i_genre_name || ' not found'); end if; end; return l_id; end get_genre_id;
  • 39. © Oren Nakdimon © Oren Nakdimon function get_genre_id ( i_genre_name in lov_genres.name%type, i_add_if_not_exists in boolean default true ) return lov_genres.id%type is l_clean_name lov_genres.name%type := initcap(trim(i_genre_name)); l_id lov_genres.id%type; begin if i_genre_name is null then return null; end if; begin select g.id into l_id from lov_genres g where g.name = i_genre_name; exception when no_data_found then if i_add_if_not_exists then begin insert into lov_genres (name) values (i_genre_name) returning id into l_id; exception when dup_val_on_index then l_id := get_genre_id( i_genre_name => l_clean_name, i_add_if_not_exists => false); end; else raise_application_error(-20000, i_genre_name || ' not found'); end if; end; return l_id; end get_genre_id;
  • 40. © Oren Nakdimon © Oren Nakdimon function get_genre_id ( i_genre_name in lov_genres.name%type, i_add_if_not_exists in boolean default true ) return lov_genres.id%type is l_clean_name lov_genres.name%type := initcap(trim(i_genre_name)); l_id lov_genres.id%type; begin if i_genre_name is null then return null; end if; begin select g.id into l_id from lov_genres g where g.name = i_genre_name; exception when no_data_found then if i_add_if_not_exists then begin insert into lov_genres (name) values (i_genre_name) returning id into l_id; exception when dup_val_on_index then l_id := get_genre_id( i_genre_name => l_clean_name, i_add_if_not_exists => false); end; else raise_application_error(-20000, i_genre_name || ' not found'); end if; end; return l_id; end get_genre_id;
  • 41. © Oren Nakdimon © Oren Nakdimon function get_genre_id ( i_genre_name in lov_genres.name%type, i_add_if_not_exists in boolean default true ) return lov_genres.id%type is l_clean_name lov_genres.name%type := initcap(trim(i_genre_name)); l_id lov_genres.id%type; begin if i_genre_name is null then return null; end if; begin select g.id into l_id from lov_genres g where g.name = i_genre_name; exception when no_data_found then if i_add_if_not_exists then begin insert into lov_genres (name) values (i_genre_name) returning id into l_id; exception when dup_val_on_index then l_id := get_genre_id( i_genre_name => l_clean_name, i_add_if_not_exists => false); end; else raise_application_error(-20000, i_genre_name || ' not found'); end if; end; return l_id; end get_genre_id;
  • 42. © Oren Nakdimon © Oren Nakdimon function get_genre_id ( i_genre_name in lov_genres.name%type, i_add_if_not_exists in boolean default true ) return lov_genres.id%type is l_clean_name lov_genres.name%type := initcap(trim(i_genre_name)); l_id lov_genres.id%type; begin if i_genre_name is null then return null; end if; begin select g.id into l_id from lov_genres g where g.name = i_genre_name; exception when no_data_found then if i_add_if_not_exists then begin insert into lov_genres (name) values (i_genre_name) returning id into l_id; exception when dup_val_on_index then l_id := get_genre_id( i_genre_name => l_clean_name, i_add_if_not_exists => false); end; else raise_application_error(-20000, i_genre_name || ' not found'); end if; end; return l_id; end get_genre_id;
  • 43. © Oren Nakdimon © Oren Nakdimon function get_genre_id ( i_genre_name in lov_genres.name%type, i_add_if_not_exists in boolean default true ) return lov_genres.id%type is l_clean_name lov_genres.name%type := initcap(trim(i_genre_name)); l_id lov_genres.id%type; begin if i_genre_name is null then return null; end if; begin select g.id into l_id from lov_genres g where g.name = i_genre_name; exception when no_data_found then if i_add_if_not_exists then begin insert into lov_genres (name) values (i_genre_name) returning id into l_id; exception when dup_val_on_index then l_id := get_genre_id( i_genre_name => l_clean_name, i_add_if_not_exists => false); end; else raise_application_error(-20000, i_genre_name || ' not found'); end if; end; return l_id; end get_genre_id; @lov1
  • 44. © Oren Nakdimon © Oren Nakdimon Look-up a reference table and add new values if not exist Get genre id by its name. If it doesn’t exist, add it. ➢Name Standardization
  • 45. © Oren Nakdimon © Oren Nakdimon create table lov_genres ( id integer generated as identity not null constraint lov_genres_pk primary key, name varchar2(20) not null ); alter table lov_genres add constraint lov_genres_name_chk check (name = initcap(trim(name)));
  • 46. © Oren Nakdimon © Oren Nakdimon function get_genre_id ( i_genre_name in lov_genres.name%type, i_add_if_not_exists in boolean default true ) return lov_genres.id%type is l_clean_name lov_genres.name%type := initcap(trim(i_genre_name)); l_id lov_genres.id%type; begin if i_genre_name is null then return null; end if; begin select g.id into l_id from lov_genres g where g.name = l_clean_name; exception when no_data_found then if i_add_if_not_exists then begin insert into lov_genres (name) values (l_clean_name) returning id into l_id; exception when dup_val_on_index then l_id := get_genre_id( i_genre_name => l_clean_name, i_add_if_not_exists => false); end; else raise_application_error(-20000, i_genre_name || ' not found'); end if; end; return l_id; end get_genre_id; @lov2
  • 47. © Oren Nakdimon © Oren Nakdimon Look-up a reference table and add new values if not exist Get genre id by its name. If it doesn’t exist, add it. ➢Name Standardization ➢Uniqueness
  • 48. © Oren Nakdimon © Oren Nakdimon create table lov_genres ( id integer generated as identity not null constraint lov_genres_pk primary key, name varchar2(20) not null ); alter table lov_genres add constraint lov_genres_name_chk check (name = initcap(trim(name))); alter table lov_genres add constraint lov_genres_name_uk unique (name); @lov3
  • 49. © Oren Nakdimon © Oren Nakdimon Look-up a reference table and add new values if not exist Get genre id by its name. If it doesn’t exist, add it. ➢Name Standardization ➢Uniqueness ➢Concurrency Control
  • 50. © Oren Nakdimon © Oren Nakdimon function get_genre_id ( i_genre_name in lov_genres.name%type, i_add_if_not_exists in boolean default true ) return lov_genres.id%type is l_clean_name lov_genres.name%type := initcap(trim(i_genre_name)); l_id lov_genres.id%type; begin if i_genre_name is null then return null; end if; begin select g.id into l_id from lov_genres g where g.name = l_clean_name; exception when no_data_found then if i_add_if_not_exists then begin insert into lov_genres (name) values (l_clean_name) returning id into l_id; exception when dup_val_on_index then l_id := get_genre_id( i_genre_name => l_clean_name, i_add_if_not_exists => false); end; else raise_application_error(-20000, i_genre_name || ' not found'); end if; end; return l_id; end get_genre_id; @lov4
  • 52. © Oren Nakdimon © Oren Nakdimon create table songs ( album_id not null constraint songs_fk_album_id references albums on delete cascade, track# number(2) not null, constraint songs_pk primary key (album_id,track#), title varchar2(100) not null, artist_id not null constraint songs_fk_atrist_id references artists, is_album_favourite number(1) default 0 not null constraint songs_favourite_boolean_chk check (is_album_favourite in (0,1)) ); create index songs_artist_id_ix on songs (artist_id) compress;
  • 53. © Oren Nakdimon © Oren Nakdimon create table songs ( album_id not null constraint songs_fk_album_id references albums on delete cascade, track# number(2) not null, constraint songs_pk primary key (album_id,track#), title varchar2(100) not null, artist_id not null constraint songs_fk_atrist_id references artists, is_album_favourite number(1) default 0 not null constraint songs_favourite_boolean_chk check (is_album_favourite in (0,1)) ); create index songs_artist_id_ix on songs (artist_id) compress; Mini-Pattern Implementing a Boolean attribute
  • 54. © Oren Nakdimon © Oren Nakdimon create table songs ( album_id not null constraint songs_fk_album_id references albums on delete cascade, track# number(2) not null, constraint songs_pk primary key (album_id,track#), title varchar2(100) not null, artist_id not null constraint songs_fk_atrist_id references artists, is_album_favourite number(1) default 0 not null constraint songs_favourite_boolean_chk check (is_album_favourite in (0,1)) ); create index songs_artist_id_ix on songs (artist_id) compress; Mini-Pattern Implementing a weak entity
  • 55. © Oren Nakdimon © Oren Nakdimon Pass multiple entities to procedures Add an album with its songs ➢Related Entities
  • 56. © Oren Nakdimon © Oren Nakdimon create or replace package music is ... procedure add_album ( i_title in albums.title%type, i_release_date in albums.release_date%type, i_genre_name in lov_genres.name%type default null, i_songs in ???, o_album_id out albums.id%type ); ... end music;
  • 57. © Oren Nakdimon © Oren Nakdimon create type song_t as object ( track# number(2), title varchar2(100), artist_id integer ) /
  • 58. © Oren Nakdimon © Oren Nakdimon create type song_t as object ( track# number(2), title varchar2(100), artist_id integer ) / create type song_tt as table of song_t /
  • 59. © Oren Nakdimon © Oren Nakdimon create type song_t as object ( track# number(2), title varchar2(100), artist_id integer ) / create type song_tt as table of song_t / create or replace package music is ... procedure add_album ( i_title in albums.title%type, i_release_date in albums.release_date%type, i_genre_name in lov_genres.name%type default null, i_songs in song_tt, o_album_id out albums.id%type ); ... end music;
  • 60. © Oren Nakdimon © Oren Nakdimon Pass multiple entities to procedures Add an album with its songs ➢Related Entities ➢Performance – less roundtrips
  • 61. © Oren Nakdimon © Oren Nakdimon create or replace package body music is ... procedure add_album ( i_title in albums.title%type, i_release_date in albums.release_date%type, i_genre_name in lov_genres.name%type default null, o_album_id out albums.id%type ) is l_genre_id lov_genres.id%type; begin l_genre_id := get_genre_id(i_genre_name); insert into albums (title, release_date, genre_id) values (i_title, i_release_date, l_genre_id) returning id into o_album_id; end add_album;
  • 62. © Oren Nakdimon © Oren Nakdimon procedure add_album ( i_title in albums.title%type, i_release_date in albums.release_date%type, i_genre_name in lov_genres.name%type default null, i_songs in song_tt, o_album_id out albums.id%type ) is l_genre_id lov_genres.id%type; begin l_genre_id := get_genre_id(i_genre_name); insert into albums (title, release_date, genre_id) values (i_title, i_release_date, l_genre_id) returning id into o_album_id; forall i in indices of i_songs insert into songs (album_id, track#, title, artist_id) values (o_album_id, i_songs(i).track#, i_songs(i).title, i_songs(i).artist_id); end add_album;
  • 63. © Oren Nakdimon © Oren Nakdimon Pass multiple entities to procedures Add an album with its songs ➢Related Entities ➢Performance – less roundtrips ➢Performance – less context switches ➢Atomicity @coll1
  • 65. © Oren Nakdimon © Oren Nakdimon Enforce the rule: an album must have at least one song ALBUM SONG
  • 66. © Oren Nakdimon © Oren Nakdimon Enforce the rule: an album must have at least one song ALBUM SONG
  • 67. © Oren Nakdimon © Oren Nakdimon Enforce the rule: an album must have at least one song ALBUM SONG
  • 68. © Oren Nakdimon © Oren Nakdimon Enforce the rule: an album must have at least one song ALBUM SONG Whenever some DML is done on some table T, another piece of code should be executed Complex Validity Check Logging Maintaining Denormalized Entities
  • 69. © Oren Nakdimon © Oren Nakdimon procedure add_album ( i_title in albums.title%type, i_release_date in albums.release_date%type, i_genre_name in lov_genres.name%type default null, i_songs in song_tt, o_album_id out albums.id%type) is l_genre_id lov_genres.id%type; begin if i_songs is null or i_songs is empty then raise_application_error( -20000, 'an album must have songs'); end if; l_genre_id := get_genre_id(i_genre_name); set_album_songs_integrity_enforced(true); insert into albums (title, release_date, genre_id) values (i_title, i_release_date, l_genre_id) returning id into o_album_id; forall i in indices of i_songs insert into songs (album_id, track#, title, artist_id) values (o_album_id, i_songs(i).track#, i_songs(i).title, i_songs(i).artist_id); set_album_songs_integrity_enforced (false); exception when others then set_album_songs_integrity_enforced (false); raise; end add_album;
  • 70. © Oren Nakdimon © Oren Nakdimon procedure add_album ( i_title in albums.title%type, i_release_date in albums.release_date%type, i_genre_name in lov_genres.name%type default null, i_songs in song_tt, o_album_id out albums.id%type) is l_genre_id lov_genres.id%type; begin if i_songs is null or i_songs is empty then raise_application_error( -20000, 'an album must have songs'); end if; l_genre_id := get_genre_id(i_genre_name); set_album_songs_integrity_enforced(true); insert into albums (title, release_date, genre_id) values (i_title, i_release_date, l_genre_id) returning id into o_album_id; forall i in indices of i_songs insert into songs (album_id, track#, title, artist_id) values (o_album_id, i_songs(i).track#, i_songs(i).title, i_songs(i).artist_id); set_album_songs_integrity_enforced (false); exception when others then set_album_songs_integrity_enforced (false); raise; end add_album;
  • 71. create or replace package body music is g_album_songs_integrity_enforced boolean not null := false; function is_album_songs_integrity_enforced return boolean is begin return g_album_songs_integrity_enforced; end is_album_songs_integrity_enforced; procedure set_album_songs_integrity_enforced(i_is_enforced in boolean) is begin g_album_songs_integrity_enforced := i_is_enforced; end set_album_songs_integrity_enforced; ... end music;
  • 72. © Oren Nakdimon © Oren Nakdimon create or replace package body music is g_album_songs_integrity_enforced boolean not null := false; function is_album_songs_integrity_enforced return boolean is begin return g_album_songs_integrity_enforced; end is_album_songs_integrity_enforced; procedure set_album_songs_integrity_enforced(i_is_enforced in boolean) is begin g_album_songs_integrity_enforced := i_is_enforced; end set_album_songs_integrity_enforced; ... end music;
  • 73. © Oren Nakdimon © Oren Nakdimon create or replace package body music is g_album_songs_integrity_enforced boolean not null := false; function is_album_songs_integrity_enforced return boolean is begin return g_album_songs_integrity_enforced; end is_album_songs_integrity_enforced; procedure set_album_songs_integrity_enforced(i_is_enforced in boolean) is begin g_album_songs_integrity_enforced := i_is_enforced; end set_album_songs_integrity_enforced; ... end music;
  • 74. © Oren Nakdimon © Oren Nakdimon create or replace package body music is g_album_songs_integrity_enforced boolean not null := false; function is_album_songs_integrity_enforced return boolean is begin return g_album_songs_integrity_enforced; end is_album_songs_integrity_enforced; procedure set_album_songs_integrity_enforced(i_is_enforced in boolean) is begin g_album_songs_integrity_enforced := i_is_enforced; end set_album_songs_integrity_enforced; ... end music;
  • 75. © Oren Nakdimon © Oren Nakdimon create or replace package body music is g_album_songs_integrity_enforced boolean not null := false; function is_album_songs_integrity_enforced return boolean is begin return g_album_songs_integrity_enforced; end is_album_songs_integrity_enforced; procedure set_album_songs_integrity_enforced(i_is_enforced in boolean) is begin g_album_songs_integrity_enforced := i_is_enforced; end set_album_songs_integrity_enforced; ... end music; create or replace package music is function is_album_songs_integrity_enforced return Boolean; ... end music;
  • 76. © Oren Nakdimon © Oren Nakdimon create or replace trigger albums_guardian_trigger before insert on albums begin if not music.is_album_songs_integrity_enforced then raise_application_error( -20000, 'to maintain albums and songs, please use the procedures in the music package'); end if; end albums_guardian_trigger; / create or replace trigger songs_guardian_trigger before delete or update of album_id on songs begin if not music.is_album_songs_integrity_enforced then raise_application_error( -20000, 'to maintain albums and songs, please use the procedures in the music package'); end if; end songs_guardian_trigger; /
  • 77. © Oren Nakdimon © Oren Nakdimon procedure add_album ( i_title in albums.title%type, i_release_date in albums.release_date%type, i_genre_name in lov_genres.name%type default null, i_songs in song_tt, o_album_id out albums.id%type) is l_genre_id lov_genres.id%type; begin if i_songs is null or i_songs is empty then raise_application_error( -20000, 'an album must have songs'); end if; l_genre_id := get_genre_id(i_genre_name); set_album_songs_integrity_enforced(true); insert into albums (title, release_date, genre_id) values (i_title, i_release_date, l_genre_id) returning id into o_album_id; forall i in indices of i_songs insert into songs (album_id, track#, title, artist_id) values (o_album_id, i_songs(i).track#, i_songs(i).title, i_songs(i).artist_id); set_album_songs_integrity_enforced (false); exception when others then set_album_songs_integrity_enforced (false); raise; end add_album;
  • 78. © Oren Nakdimon © Oren Nakdimon procedure add_album ( i_title in albums.title%type, i_release_date in albums.release_date%type, i_genre_name in lov_genres.name%type default null, i_songs in song_tt, o_album_id out albums.id%type) is l_genre_id lov_genres.id%type; begin if i_songs is null or i_songs is empty then raise_application_error( -20000, 'an album must have songs'); end if; l_genre_id := get_genre_id(i_genre_name); set_album_songs_integrity_enforced(true); insert into albums (title, release_date, genre_id) values (i_title, i_release_date, l_genre_id) returning id into o_album_id; forall i in indices of i_songs insert into songs (album_id, track#, title, artist_id) values (o_album_id, i_songs(i).track#, i_songs(i).title, i_songs(i).artist_id); set_album_songs_integrity_enforced (false); exception when others then set_album_songs_integrity_enforced (false); raise; end add_album;
  • 79. © Oren Nakdimon © Oren Nakdimon procedure add_album ( i_title in albums.title%type, i_release_date in albums.release_date%type, i_genre_name in lov_genres.name%type default null, i_songs in song_tt, o_album_id out albums.id%type) is l_genre_id lov_genres.id%type; begin if i_songs is null or i_songs is empty then raise_application_error( -20000, 'an album must have songs'); end if; l_genre_id := get_genre_id(i_genre_name); set_album_songs_integrity_enforced(true); insert into albums (title, release_date, genre_id) values (i_title, i_release_date, l_genre_id) returning id into o_album_id; forall i in indices of i_songs insert into songs (album_id, track#, title, artist_id) values (o_album_id, i_songs(i).track#, i_songs(i).title, i_songs(i).artist_id); set_album_songs_integrity_enforced (false); exception when others then set_album_songs_integrity_enforced (false); raise; end add_album; @trig
  • 80. © Oren Nakdimon © Oren Nakdimon The Suggested Solution  Put the logic in a procedure (or procedures)  Use a global variable that indicates if the DML is allowed  Initialize it to FALSE  Set it to TRUE only for the duration of the procedure  A statement-level trigger on the DML verifies the flag is on
  • 82. © Oren Nakdimon © Oren Nakdimon ALBUM # id * title * release_date SONG # track# * title o is_album_favourite ARTIST # id * name * type * date_of_birth o date_of_death LOV_GENRE # id * name IMAGE # id * name * size * resolution
  • 83. © Oren Nakdimon © Oren Nakdimon Implement an arc relationship from Images to Artists and Albums Implement an arc relationship from a generic entity
  • 84. © Oren Nakdimon © Oren Nakdimon Option #1 – Multiple Dedicated Tables ALBUM_IMAGES # image_id * album_id (FK) * name * size * resolution . . . ARTIST_IMAGES # image_id * artist_id (FK) * name * size * resolution . . . ?_IMAGES # image_id * ?_id (FK) * name * size * resolution . . . Code Code Code
  • 85. © Oren Nakdimon © Oren Nakdimon Option #2 – A Single Table with Dedicated Columns IMAGES # image_id o album_id (FK) o artist_id (FK) * name * size * resolution . . . CHECK (album_id is null and artist_id is not null or album_id is not null and artist_id is null)
  • 86. © Oren Nakdimon © Oren Nakdimon CHECK (nvl2(album_id,1,0)+nvl2(artist_id,1,0)=1) Option #2 – A Single Table with Dedicated Columns IMAGES # image_id o album_id (FK) o artist_id (FK) * name * size * resolution . . . Code album/artist
  • 87. © Oren Nakdimon © Oren Nakdimon Option #3 – A Generic Table IMAGES # image_id o owner_type o owner_id * name * size * resolution . . . Code check (owner_type in ('Artist','Album')) No Referential Integrity
  • 88. © Oren Nakdimon © Oren Nakdimon The Suggested Solution – Option #4  The generic table solution (option #3)  + virtual columns to enforce referential integrity
  • 89. © Oren Nakdimon © Oren Nakdimon create table images ( id integer generated as identity not null constraint images_pk primary key, name varchar2(200) not null, size_in_bytes integer not null, resolution integer not null, --- ... owner_type varchar2(6) not null constraint images_owner_type_chk check (owner_type in ('Artist','Album')), owner_id integer not null, artist_id invisible generated always as (decode(owner_type,'Artist',owner_id)) virtual constraint images_fk_artist_id references artists(id) on delete cascade, album_id invisible generated always as (decode(owner_type,'Album',owner_id)) virtual constraint images_fk_album_id references albums(id) on delete cascade );
  • 90. © Oren Nakdimon © Oren Nakdimon create table images ( id integer generated as identity not null constraint images_pk primary key, name varchar2(200) not null, size_in_bytes integer not null, resolution integer not null, --- ... owner_type varchar2(6) not null constraint images_owner_type_chk check (owner_type in ('Artist','Album')), owner_id integer not null, artist_id invisible generated always as (decode(owner_type,'Artist',owner_id)) virtual constraint images_fk_artist_id references artists(id) on delete cascade, album_id invisible generated always as (decode(owner_type,'Album',owner_id)) virtual constraint images_fk_album_id references albums(id) on delete cascade );
  • 91. © Oren Nakdimon © Oren Nakdimon create table images ( id integer generated as identity not null constraint images_pk primary key, name varchar2(200) not null, size_in_bytes integer not null, resolution integer not null, --- ... owner_type varchar2(6) not null constraint images_owner_type_chk check (owner_type in ('Artist','Album')), owner_id integer not null, artist_id invisible generated always as (decode(owner_type,'Artist',owner_id)) virtual constraint images_fk_artist_id references artists(id) on delete cascade, album_id invisible generated always as (decode(owner_type,'Album',owner_id)) virtual constraint images_fk_album_id references albums(id) on delete cascade );
  • 92. © Oren Nakdimon © Oren Nakdimon create table images ( id integer generated as identity not null constraint images_pk primary key, name varchar2(200) not null, size_in_bytes integer not null, resolution integer not null, --- ... owner_type varchar2(6) not null constraint images_owner_type_chk check (owner_type in ('Artist','Album')), owner_id integer not null, artist_id invisible generated always as (decode(owner_type,'Artist',owner_id)) virtual constraint images_fk_artist_id references artists(id) on delete cascade, album_id invisible generated always as (decode(owner_type,'Album',owner_id)) virtual constraint images_fk_album_id references albums(id) on delete cascade );
  • 93. © Oren Nakdimon © Oren Nakdimon create table images ( id integer generated as identity not null constraint images_pk primary key, name varchar2(200) not null, size_in_bytes integer not null, resolution integer not null, --- ... owner_type varchar2(6) not null constraint images_owner_type_chk check (owner_type in ('Artist','Album')), owner_id integer not null, artist_id invisible generated always as (decode(owner_type,'Artist',owner_id)) virtual constraint images_fk_artist_id references artists(id) on delete cascade, album_id invisible generated always as (decode(owner_type,'Album',owner_id)) virtual constraint images_fk_album_id references albums(id) on delete cascade );
  • 94. © Oren Nakdimon © Oren Nakdimon create table images ( id integer generated as identity not null constraint images_pk primary key, name varchar2(200) not null, size_in_bytes integer not null, resolution integer not null, --- ... owner_type varchar2(6) not null constraint images_owner_type_chk check (owner_type in ('Artist','Album')), owner_id integer not null, artist_id invisible generated always as (decode(owner_type,'Artist',owner_id)) virtual constraint images_fk_artist_id references artists(id) on delete cascade, album_id invisible generated always as (decode(owner_type,'Album',owner_id)) virtual constraint images_fk_album_id references albums(id) on delete cascade );
  • 95. © Oren Nakdimon © Oren Nakdimon create table images ( id integer generated as identity not null constraint images_pk primary key, name varchar2(200) not null, size_in_bytes integer not null, resolution integer not null, --- ... owner_type varchar2(6) not null constraint images_owner_type_chk check (owner_type in ('Artist','Album')), owner_id integer not null, artist_id invisible generated always as (decode(owner_type,'Artist',owner_id)) virtual constraint images_fk_artist_id references artists(id) on delete cascade, album_id invisible generated always as (decode(owner_type,'Album',owner_id)) virtual constraint images_fk_album_id references albums(id) on delete cascade );
  • 96. © Oren Nakdimon © Oren Nakdimon create or replace package images_dl is procedure add_image ( i_owner_type in images.owner_type%type, i_owner_id in images.owner_id%type, i_name in images.name%type, i_size in images.size_in_bytes%type, i_resolution in images.resolution%type, o_id out images.id%type ); procedure delete_image(i_id in images.id%type); procedure get_images ( i_owner_type in images.owner_type%type, i_owner_id in images.owner_id%type, o_images_rc out sys_refcursor ); end images_dl;
  • 97. © Oren Nakdimon © Oren Nakdimon procedure add_image ( i_owner_type in images.owner_type%type, i_owner_id in images.owner_id%type, i_name in images.name%type, i_size in images.size_in_bytes%type, i_resolution in images.resolution%type, o_id out images.id%type ) is begin insert into images (name, size_in_bytes, resolution, owner_type, owner_id) values (i_name, i_size, i_resolution, i_owner_type, i_owner_id) returning id into o_id; end add_image;
  • 98. © Oren Nakdimon © Oren Nakdimon procedure delete_image(i_id in images.id%type) is begin delete images i where i.id = i_id; end delete_image;
  • 99. © Oren Nakdimon © Oren Nakdimon procedure get_images ( i_owner_type in images.owner_type%type, i_owner_id in images.owner_id%type, o_images_rc out sys_refcursor ) is begin open o_images_rc for select i.owner_id, i.id, i.name, i.size_in_bytes, i.resolution from images i where i.owner_type = i_owner_type and i.owner_id = i_owner_id order by i.id; end get_images;
  • 100. © Oren Nakdimon © Oren Nakdimon create or replace package music is ... procedure get_artist ( i_id in artists.id%type, o_name out artists.name%type, o_type out artists.type%type, o_date_of_birth out artists.date_of_birth%type, o_date_of_death out artists.date_of_death%type, o_images_rc out sys_refcursor ); procedure get_album ( i_id in albums.id%type, o_title out albums.title%type, o_songs_rc out sys_refcursor, o_images_rc out sys_refcursor ); ... end music;
  • 101. © Oren Nakdimon © Oren Nakdimon procedure get_artist ( i_id in artists.id%type, o_name out artists.name%type, o_type out artists.type%type, o_date_of_birth out artists.date_of_birth%type, o_date_of_death out artists.date_of_death%type, o_images_rc out sys_refcursor ) is begin select ar.name, ar.type, ar.date_of_birth, ar.date_of_death into o_name, o_type, o_date_of_birth, o_date_of_death from artists ar where ar.id = i_id; images_dl.get_images(i_owner_type => 'Artist', i_owner_id => i_id, o_images_rc => o_images_rc); end get_artist;
  • 102. © Oren Nakdimon © Oren Nakdimon procedure get_album ( i_id in albums.id%type, o_title out albums.title%type, o_songs_rc out sys_refcursor, o_images_rc out sys_refcursor ) is begin select al.title into o_title from albums al where al.id = i_id; open o_songs_rc for select s.album_id, s.track#, s.title, s.artist_id, s.is_album_favourite from songs s where s.album_id = i_id order by s.track#; images_dl.get_images(i_owner_type => 'Album', i_owner_id => i_id, o_images_rc => o_images_rc); end get_album;
  • 103. THANK YOU☺ Oren Nakdimon www.db-oriented.com  oren@db-oriented.com  +972-54-4393763 @DBoriented