SlideShare a Scribd company logo
1 of 122
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
©
Oren
Nakdimon
©
Oren
Nakdimon
http://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 living between 2 dates
©
Oren
Nakdimon
©
Oren
Nakdimon
Finding Points in a Range is Straightforward
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure get_albums
(
i_released_from in albums.release_date%type,
i_released_to in albums.release_date%type,
o_albums_rc out sys_refcursor
) is
begin
open o_albums_rc for
select al.title,
al.release_date
from albums al
where al.release_date between i_released_from and i_released_to;
end get_albums;
©
Oren
Nakdimon
©
Oren
Nakdimon
Finding Ranges that Overlap a Range
©
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
NOT (Start2>End1 or Start1>End2)
©
Oren
Nakdimon
©
Oren
Nakdimon
Overlapping Ranges
NOT (Start2>End1 or Start1>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)
);
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;
@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
)
/
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
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;
©
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
CONDITIONAL UNIQUENESS
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure set_album_favourite_song
(
i_album_id in songs.album_id%type,
i_track# in songs.track#%type,
i_is_favourite in songs.is_album_favourite%type
) is
begin
update songs s
set s.is_album_favourite = i_is_favourite
where s.album_id = i_album_id
and s.track# = i_track#;
end set_album_favourite_song;
@cond1
©
Oren
Nakdimon
©
Oren
Nakdimon
Enforce the rule: each album may have at most one favourite song
Enforce a business rule that requires uniqueness of an attribute
(column) within a subset of the entity instances (table rows)
©
Oren
Nakdimon
©
Oren
Nakdimon
The Suggested Solution
 Create a virtual column that has NULLs for the non-relevant rows
 Create a unique constraint on the virtual column
alter table songs add (
favourite_song_of_album_id as (decode(is_album_favourite,1,album_id)) virtual
constraint songs_favourite_uk unique
);
©
Oren
Nakdimon
©
Oren
Nakdimon
The Suggested Solution
 Create a virtual column that has NULLs for the non-relevant rows
 Create a unique constraint on the virtual column
alter table songs add (
favourite_song_of_album_id as (decode(is_album_favourite,1,album_id)) virtual
constraint songs_favourite_uk unique
);
©
Oren
Nakdimon
©
Oren
Nakdimon
The Suggested Solution
 Create a virtual column that has NULLs for the non-relevant rows
 Create a unique constraint on the virtual column
alter table songs add (
favourite_song_of_album_id as (decode(is_album_favourite,1,album_id)) virtual
constraint songs_favourite_uk unique
);
@cond2
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure unset_album_favourite_song(i_album_id in songs.album_id%type) is
begin
update songs s
set s.is_album_favourite = 0
where s.favourite_song_of_album_id = i_album_id;
end unset_album_favourite_song;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure unset_album_favourite_song(i_album_id in songs.album_id%type) is
begin
update songs s
set s.is_album_favourite = 0
where s.favourite_song_of_album_id = i_album_id;
end unset_album_favourite_song;
-----------------------------------------------------------
| Id | Operation | Name |
-----------------------------------------------------------
| 0 | UPDATE STATEMENT | |
| 1 | UPDATE | SONGS |
| 2 | TABLE ACCESS BY INDEX ROWID| SONGS |
|* 3 | INDEX UNIQUE SCAN | SONGS_FAVOURITE_UK |
-----------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("S"."FAVOURITE_SONG_OF_ALBUM_ID"=TO_NUMBER(:I_ALBUM_ID))
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure unset_album_favourite_song(i_album_id in songs.album_id%type) is
begin
update songs s
set s.is_album_favourite = 0
where s.favourite_song_of_album_id = i_album_id;
end unset_album_favourite_song;
-----------------------------------------------------------
| Id | Operation | Name |
-----------------------------------------------------------
| 0 | UPDATE STATEMENT | |
| 1 | UPDATE | SONGS |
| 2 | TABLE ACCESS BY INDEX ROWID| SONGS |
|* 3 | INDEX UNIQUE SCAN | SONGS_FAVOURITE_UK |
-----------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("S"."FAVOURITE_SONG_OF_ALBUM_ID"=TO_NUMBER(:I_ALBUM_ID))
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure unset_album_favourite_song(i_album_id in songs.album_id%type) is
begin
update songs s
set s.is_album_favourite = 0
where s.favourite_song_of_album_id = i_album_id;
end unset_album_favourite_song;
procedure set_album_favourite_song
(
i_album_id in songs.album_id%type,
i_track# in songs.track#%type
) is
begin
unset_album_favourite_song(i_album_id => i_album_id);
update songs s
set s.is_album_favourite = 1
where s.album_id = i_album_id
and s.track# = i_track#;
end set_album_favourite_song;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure unset_album_favourite_song(i_album_id in songs.album_id%type) is
begin
update songs s
set s.is_album_favourite = 0
where s.favourite_song_of_album_id = i_album_id;
end unset_album_favourite_song;
procedure set_album_favourite_song
(
i_album_id in songs.album_id%type,
i_track# in songs.track#%type
) is
begin
unset_album_favourite_song(i_album_id => i_album_id);
update songs s
set s.is_album_favourite = 1
where s.album_id = i_album_id
and s.track# = i_track#;
end set_album_favourite_song;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure unset_album_favourite_song(i_album_id in songs.album_id%type) is
begin
update songs s
set s.is_album_favourite = 0
where s.favourite_song_of_album_id = i_album_id;
end unset_album_favourite_song;
procedure set_album_favourite_song
(
i_album_id in songs.album_id%type,
i_track# in songs.track#%type
) is
begin
unset_album_favourite_song(i_album_id => i_album_id);
update songs s
set s.is_album_favourite = 1
where s.album_id = i_album_id
and s.track# = i_track#;
end set_album_favourite_song;
CORRELATED RESULTS
©
Oren
Nakdimon
©
Oren
Nakdimon
Return albums – and all their songs – based on various inputs
©
Oren
Nakdimon
©
Oren
Nakdimon
Return albums – and all their songs – based on various inputs
procedure get_albums
(
i_artist_name in artists.name%type default null,
i_genre_id_list in int_tt default null,
i_released_after in albums.release_date%type default null,
i_released_before in albums.release_date%type default null,
o_albums_rc out sys_refcursor,
o_songs_rc out sys_refcursor
);
©
Oren
Nakdimon
©
Oren
Nakdimon
Return albums – and all their songs – based on various inputs
procedure get_albums
(
i_artist_name in artists.name%type default null,
i_genre_id_list in int_tt default null,
i_released_after in albums.release_date%type default null,
i_released_before in albums.release_date%type default null,
o_albums_rc out sys_refcursor,
o_songs_rc out sys_refcursor
);
create type int_tt as table of integer
©
Oren
Nakdimon
©
Oren
Nakdimon
Return albums – and all their songs – based on various inputs
procedure get_albums
(
i_artist_name in artists.name%type default null,
i_genre_id_list in int_tt default null,
i_released_after in albums.release_date%type default null,
i_released_before in albums.release_date%type default null,
o_albums_rc out sys_refcursor,
o_songs_rc out sys_refcursor
);
©
Oren
Nakdimon
©
Oren
Nakdimon
Return albums – and all their songs – based on various inputs
procedure get_albums
(
i_artist_name in artists.name%type default null,
i_genre_id_list in int_tt default null,
i_released_after in albums.release_date%type default null,
i_released_before in albums.release_date%type default null,
o_albums_rc out sys_refcursor,
o_songs_rc out sys_refcursor
);
procedure get_albums (
i_artist_name in artists.name%type default null,
i_genre_id_list in int_tt default null,
i_released_after in albums.release_date%type default null,
i_released_before in albums.release_date%type default null,
o_albums_rc out sys_refcursor,
o_songs_rc out sys_refcursor)
is
begin
open o_albums_rc for
select al.id,
al.title,
al.release_date,
al.genre_id
from albums al
where ......................
order by al.id;
open o_songs_rc for
select s.album_id,
s.track#,
s.title,
s.artist_id,
s.is_album_favourite
from albums al,
songs s
where ......................
and s.album_id = al.id
order by s.album_id,
s.track#;
end get_albums;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure get_albums (
i_artist_name in artists.name%type default null,
i_genre_id_list in int_tt default null,
i_released_after in albums.release_date%type default null,
i_released_before in albums.release_date%type default null,
o_albums_rc out sys_refcursor,
o_songs_rc out sys_refcursor)
is
pragma autonomous_transaction;
l_album_id_list int_tt;
begin
set transaction read only;
select al.id
bulk collect into l_album_id_list
from albums al
where (i_artist_name is null or
exists (select null from songs s, artists ar
where s.album_id = al.id
and instr(ar.name, i_artist_name) > 0
and s.artist_id = ar.id))
and (i_genre_id_list is null or al.genre_id member of i_genre_id_list)
and al.release_date >= nvl(i_released_after, date '0001-01-01')
and al.release_date <= nvl(i_released_before, date '9999-12-31');
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure get_albums (
i_artist_name in artists.name%type default null,
i_genre_id_list in int_tt default null,
i_released_after in albums.release_date%type default null,
i_released_before in albums.release_date%type default null,
o_albums_rc out sys_refcursor,
o_songs_rc out sys_refcursor)
is
pragma autonomous_transaction;
l_album_id_list int_tt;
begin
set transaction read only;
select al.id
bulk collect into l_album_id_list
from albums al
where (i_artist_name is null or
exists (select null from songs s, artists ar
where s.album_id = al.id
and instr(ar.name, i_artist_name) > 0
and s.artist_id = ar.id))
and (i_genre_id_list is null or al.genre_id member of i_genre_id_list)
and al.release_date >= nvl(i_released_after, date '0001-01-01')
and al.release_date <= nvl(i_released_before, date '9999-12-31');
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure get_albums (
i_artist_name in artists.name%type default null,
i_genre_id_list in int_tt default null,
i_released_after in albums.release_date%type default null,
i_released_before in albums.release_date%type default null,
o_albums_rc out sys_refcursor,
o_songs_rc out sys_refcursor)
is
pragma autonomous_transaction;
l_album_id_list int_tt;
begin
set transaction read only;
select al.id
bulk collect into l_album_id_list
from albums al
where (i_artist_name is null or
exists (select null from songs s, artists ar
where s.album_id = al.id
and instr(ar.name, i_artist_name) > 0
and s.artist_id = ar.id))
and (i_genre_id_list is null or al.genre_id member of i_genre_id_list)
and al.release_date >= nvl(i_released_after, date '0001-01-01')
and al.release_date <= nvl(i_released_before, date '9999-12-31');
open o_albums_rc for
select a.id, a.title, a.release_date, a.genre_id
from table(l_album_id_list) c,
albums a
where a.id = c.column_value
order by a.id;
open o_songs_rc for
select s.album_id, s.track#, s.title, s.artist_id,
s.is_album_favourite
from table(l_album_id_list) c,
songs s
where s.album_id = c.column_value
order by s.album_id,
s.track#;
commit;
end get_albums;
©
Oren
Nakdimon
©
Oren
Nakdimon
procedure get_albums (
i_artist_name in artists.name%type default null,
i_genre_id_list in int_tt default null,
i_released_after in albums.release_date%type default null,
i_released_before in albums.release_date%type default null,
o_albums_rc out sys_refcursor,
o_songs_rc out sys_refcursor)
is
pragma autonomous_transaction;
l_album_id_list int_tt;
begin
set transaction read only;
select al.id
bulk collect into l_album_id_list
from albums al
where (i_artist_name is null or
exists (select null from songs s, artists ar
where s.album_id = al.id
and instr(ar.name, i_artist_name) > 0
and s.artist_id = ar.id))
and (i_genre_id_list is null or al.genre_id member of i_genre_id_list)
and al.release_date >= nvl(i_released_after, date '0001-01-01')
and al.release_date <= nvl(i_released_before, date '9999-12-31');
open o_albums_rc for
select a.id, a.title, a.release_date, a.genre_id
from table(l_album_id_list) c,
albums a
where a.id = c.column_value
order by a.id;
open o_songs_rc for
select s.album_id, s.track#, s.title, s.artist_id,
s.is_album_favourite
from table(l_album_id_list) c,
songs s
where s.album_id = c.column_value
order by s.album_id,
s.track#;
commit;
end get_albums;
ARC RELATIONSHIP
©
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
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
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;

More Related Content

Similar to Oren nakdimon - Design Patterns for PL/SQL and SQL - UKOUGtogether21

Design Patterns in PLSQL and SQL [UKOUG 22]
Design Patterns in PLSQL and SQL [UKOUG 22]Design Patterns in PLSQL and SQL [UKOUG 22]
Design Patterns in PLSQL and SQL [UKOUG 22]Oren Nakdimon
 
Oracle Inter-Session Communication
Oracle Inter-Session CommunicationOracle Inter-Session Communication
Oracle Inter-Session CommunicationOren Nakdimon
 
Constraint Optimization
Constraint OptimizationConstraint Optimization
Constraint OptimizationOren Nakdimon
 
Clean Code Principles
Clean Code PrinciplesClean Code Principles
Clean Code PrinciplesYeurDreamin'
 
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009Yasuko Ohba
 
Intro to Ruby - Twin Cities Code Camp 7
Intro to Ruby - Twin Cities Code Camp 7Intro to Ruby - Twin Cities Code Camp 7
Intro to Ruby - Twin Cities Code Camp 7Brian Hogan
 
Eric Lafortune - ProGuard and DexGuard for optimization and protection
Eric Lafortune - ProGuard and DexGuard for optimization and protectionEric Lafortune - ProGuard and DexGuard for optimization and protection
Eric Lafortune - ProGuard and DexGuard for optimization and protectionGuardSquare
 
Refactoring developer habits
Refactoring developer habitsRefactoring developer habits
Refactoring developer habitsMani Sarkar
 
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019Victor Rentea
 
Building Maintainable Applications in Apex
Building Maintainable Applications in ApexBuilding Maintainable Applications in Apex
Building Maintainable Applications in ApexJeffrey Kemp
 
Dealing with Legacy Perl Code - Peter Scott
Dealing with Legacy Perl Code - Peter ScottDealing with Legacy Perl Code - Peter Scott
Dealing with Legacy Perl Code - Peter ScottO'Reilly Media
 
Write Less (Code) With More (Features) [UKOUG 22]
Write Less (Code) With More (Features) [UKOUG 22]Write Less (Code) With More (Features) [UKOUG 22]
Write Less (Code) With More (Features) [UKOUG 22]Oren Nakdimon
 
Debugging and Error handling
Debugging and Error handlingDebugging and Error handling
Debugging and Error handlingSuite Solutions
 
Tackling Tech Debt with Rector
Tackling Tech Debt with RectorTackling Tech Debt with Rector
Tackling Tech Debt with RectorMichele Orselli
 

Similar to Oren nakdimon - Design Patterns for PL/SQL and SQL - UKOUGtogether21 (20)

Design Patterns in PLSQL and SQL [UKOUG 22]
Design Patterns in PLSQL and SQL [UKOUG 22]Design Patterns in PLSQL and SQL [UKOUG 22]
Design Patterns in PLSQL and SQL [UKOUG 22]
 
Oracle Inter-Session Communication
Oracle Inter-Session CommunicationOracle Inter-Session Communication
Oracle Inter-Session Communication
 
Constraint Optimization
Constraint OptimizationConstraint Optimization
Constraint Optimization
 
Clean Code Principles
Clean Code PrinciplesClean Code Principles
Clean Code Principles
 
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
 
030325+seminar+scg+iam.ppt
030325+seminar+scg+iam.ppt030325+seminar+scg+iam.ppt
030325+seminar+scg+iam.ppt
 
Intro to Ruby - Twin Cities Code Camp 7
Intro to Ruby - Twin Cities Code Camp 7Intro to Ruby - Twin Cities Code Camp 7
Intro to Ruby - Twin Cities Code Camp 7
 
Eric Lafortune - ProGuard and DexGuard for optimization and protection
Eric Lafortune - ProGuard and DexGuard for optimization and protectionEric Lafortune - ProGuard and DexGuard for optimization and protection
Eric Lafortune - ProGuard and DexGuard for optimization and protection
 
Refactoring developer habits
Refactoring developer habitsRefactoring developer habits
Refactoring developer habits
 
Visual Basic
Visual BasicVisual Basic
Visual Basic
 
Visual Basic
Visual BasicVisual Basic
Visual Basic
 
Os Harris
Os HarrisOs Harris
Os Harris
 
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
 
Building Maintainable Applications in Apex
Building Maintainable Applications in ApexBuilding Maintainable Applications in Apex
Building Maintainable Applications in Apex
 
Dealing with Legacy Perl Code - Peter Scott
Dealing with Legacy Perl Code - Peter ScottDealing with Legacy Perl Code - Peter Scott
Dealing with Legacy Perl Code - Peter Scott
 
Write Less (Code) With More (Features) [UKOUG 22]
Write Less (Code) With More (Features) [UKOUG 22]Write Less (Code) With More (Features) [UKOUG 22]
Write Less (Code) With More (Features) [UKOUG 22]
 
Legacy is Good
Legacy is GoodLegacy is Good
Legacy is Good
 
Debugging and Error handling
Debugging and Error handlingDebugging and Error handling
Debugging and Error handling
 
Tackling Tech Debt with Rector
Tackling Tech Debt with RectorTackling Tech Debt with Rector
Tackling Tech Debt with Rector
 
Clean code
Clean codeClean code
Clean code
 

More from Oren Nakdimon

Oren nakdimon - Write Less With More - UKOUGtogether21
Oren nakdimon - Write Less With More - UKOUGtogether21Oren nakdimon - Write Less With More - UKOUGtogether21
Oren nakdimon - Write Less With More - UKOUGtogether21Oren Nakdimon
 
Oren Nakdimon - Oracle SQL Pattern Matching Made Easy
Oren Nakdimon - Oracle SQL Pattern Matching Made EasyOren Nakdimon - Oracle SQL Pattern Matching Made Easy
Oren Nakdimon - Oracle SQL Pattern Matching Made EasyOren Nakdimon
 
Edition Based Redefinition Made Easy - Oren Nakdimon
Edition Based Redefinition Made Easy - Oren NakdimonEdition Based Redefinition Made Easy - Oren Nakdimon
Edition Based Redefinition Made Easy - Oren NakdimonOren Nakdimon
 
Oren nakdimon - Oracle 12c SQL Pattern Matching Made Easy
Oren nakdimon - Oracle 12c SQL Pattern Matching Made EasyOren nakdimon - Oracle 12c SQL Pattern Matching Made Easy
Oren nakdimon - Oracle 12c SQL Pattern Matching Made EasyOren Nakdimon
 
Oren nakdimon oh really... i didn't know it is supported in standard edition
Oren nakdimon   oh really... i didn't know it is supported in standard editionOren nakdimon   oh really... i didn't know it is supported in standard edition
Oren nakdimon oh really... i didn't know it is supported in standard editionOren Nakdimon
 
Write Less (code) With More (Oracle Database 12c New Features)
Write Less (code) With More (Oracle Database 12c New Features)Write Less (code) With More (Oracle Database 12c New Features)
Write Less (code) With More (Oracle Database 12c New Features)Oren Nakdimon
 
Indexes and Indexing in Oracle 12c
Indexes and Indexing in Oracle 12cIndexes and Indexing in Oracle 12c
Indexes and Indexing in Oracle 12cOren Nakdimon
 
How to upgrade your application with no downtime (using edition-based redefin...
How to upgrade your application with no downtime (using edition-based redefin...How to upgrade your application with no downtime (using edition-based redefin...
How to upgrade your application with no downtime (using edition-based redefin...Oren Nakdimon
 
The Features That (maybe) You Didn't Know About
The Features That (maybe) You Didn't Know AboutThe Features That (maybe) You Didn't Know About
The Features That (maybe) You Didn't Know AboutOren Nakdimon
 

More from Oren Nakdimon (9)

Oren nakdimon - Write Less With More - UKOUGtogether21
Oren nakdimon - Write Less With More - UKOUGtogether21Oren nakdimon - Write Less With More - UKOUGtogether21
Oren nakdimon - Write Less With More - UKOUGtogether21
 
Oren Nakdimon - Oracle SQL Pattern Matching Made Easy
Oren Nakdimon - Oracle SQL Pattern Matching Made EasyOren Nakdimon - Oracle SQL Pattern Matching Made Easy
Oren Nakdimon - Oracle SQL Pattern Matching Made Easy
 
Edition Based Redefinition Made Easy - Oren Nakdimon
Edition Based Redefinition Made Easy - Oren NakdimonEdition Based Redefinition Made Easy - Oren Nakdimon
Edition Based Redefinition Made Easy - Oren Nakdimon
 
Oren nakdimon - Oracle 12c SQL Pattern Matching Made Easy
Oren nakdimon - Oracle 12c SQL Pattern Matching Made EasyOren nakdimon - Oracle 12c SQL Pattern Matching Made Easy
Oren nakdimon - Oracle 12c SQL Pattern Matching Made Easy
 
Oren nakdimon oh really... i didn't know it is supported in standard edition
Oren nakdimon   oh really... i didn't know it is supported in standard editionOren nakdimon   oh really... i didn't know it is supported in standard edition
Oren nakdimon oh really... i didn't know it is supported in standard edition
 
Write Less (code) With More (Oracle Database 12c New Features)
Write Less (code) With More (Oracle Database 12c New Features)Write Less (code) With More (Oracle Database 12c New Features)
Write Less (code) With More (Oracle Database 12c New Features)
 
Indexes and Indexing in Oracle 12c
Indexes and Indexing in Oracle 12cIndexes and Indexing in Oracle 12c
Indexes and Indexing in Oracle 12c
 
How to upgrade your application with no downtime (using edition-based redefin...
How to upgrade your application with no downtime (using edition-based redefin...How to upgrade your application with no downtime (using edition-based redefin...
How to upgrade your application with no downtime (using edition-based redefin...
 
The Features That (maybe) You Didn't Know About
The Features That (maybe) You Didn't Know AboutThe Features That (maybe) You Didn't Know About
The Features That (maybe) You Didn't Know About
 

Recently uploaded

Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)jennyeacort
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfFerryKemperman
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentationvaddepallysandeep122
 
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in NoidaBuds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in Noidabntitsolutionsrishis
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfStefano Stabellini
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Hr365.us smith
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Natan Silnitsky
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...OnePlan Solutions
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Velvetech LLC
 

Recently uploaded (20)

Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdf
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentation
 
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in NoidaBuds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
Advantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your BusinessAdvantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your Business
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdf
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...
 

Oren nakdimon - Design Patterns for PL/SQL and SQL - UKOUGtogether21

  • 1. Design Patterns in PL/SQL and SQL Oren Nakdimon www.db-oriented.com  oren@db-oriented.com  +972-54-4393763 @DBoriented
  • 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 living between 2 dates
  • 15. © Oren Nakdimon © Oren Nakdimon procedure get_albums ( i_released_from in albums.release_date%type, i_released_to in albums.release_date%type, o_albums_rc out sys_refcursor ) is begin open o_albums_rc for select al.title, al.release_date from albums al where al.release_date between i_released_from and i_released_to; end get_albums;
  • 21. © Oren Nakdimon © Oren Nakdimon Overlapping Ranges NOT (Start2>End1 or Start1>End2) Start2<End1 and Start1<End2
  • 22. © Oren Nakdimon © Oren Nakdimon The Suggested Solution StartColumn < EndParameter and StartParameter < EndColumn
  • 23. © 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
  • 25. © Oren Nakdimon © Oren Nakdimon The Suggested Solution StartColumn < EndParameter and StartParameter < EndColumn StartColumn <= EndParameter and StartParameter <= EndColumn Exclusive Ranges Inclusive Ranges
  • 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 <= ar.date_of_death order by ar.date_of_birth, ar.name; end get_people; @over2
  • 27. © 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)) );
  • 28. © 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
  • 30. © 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 );
  • 31. © 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
  • 32. © 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;
  • 33. © 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;
  • 34. © 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.
  • 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 ) / 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;
  • 58. © Oren Nakdimon © Oren Nakdimon Pass multiple entities to procedures Add an album with its songs ➢Related Entities ➢Performance – less roundtrips
  • 59. © 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;
  • 60. © 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;
  • 61. © 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
  • 63. © Oren Nakdimon © Oren Nakdimon Enforce the rule: an album must have at least one song ALBUM SONG
  • 64. © Oren Nakdimon © Oren Nakdimon Enforce the rule: an album must have at least one song ALBUM SONG
  • 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 Whenever some DML is done on some table T, another piece of code should be executed
  • 67. 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;
  • 68. © 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;
  • 69. © 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;
  • 70. © 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;
  • 71. © 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;
  • 72. © 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; /
  • 73. © 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;
  • 74. © 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;
  • 75. © 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;
  • 76. © 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
  • 77. © 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
  • 79. © Oren Nakdimon © Oren Nakdimon procedure set_album_favourite_song ( i_album_id in songs.album_id%type, i_track# in songs.track#%type, i_is_favourite in songs.is_album_favourite%type ) is begin update songs s set s.is_album_favourite = i_is_favourite where s.album_id = i_album_id and s.track# = i_track#; end set_album_favourite_song; @cond1
  • 80. © Oren Nakdimon © Oren Nakdimon Enforce the rule: each album may have at most one favourite song Enforce a business rule that requires uniqueness of an attribute (column) within a subset of the entity instances (table rows)
  • 81. © Oren Nakdimon © Oren Nakdimon The Suggested Solution  Create a virtual column that has NULLs for the non-relevant rows  Create a unique constraint on the virtual column alter table songs add ( favourite_song_of_album_id as (decode(is_album_favourite,1,album_id)) virtual constraint songs_favourite_uk unique );
  • 82. © Oren Nakdimon © Oren Nakdimon The Suggested Solution  Create a virtual column that has NULLs for the non-relevant rows  Create a unique constraint on the virtual column alter table songs add ( favourite_song_of_album_id as (decode(is_album_favourite,1,album_id)) virtual constraint songs_favourite_uk unique );
  • 83. © Oren Nakdimon © Oren Nakdimon The Suggested Solution  Create a virtual column that has NULLs for the non-relevant rows  Create a unique constraint on the virtual column alter table songs add ( favourite_song_of_album_id as (decode(is_album_favourite,1,album_id)) virtual constraint songs_favourite_uk unique ); @cond2
  • 84. © Oren Nakdimon © Oren Nakdimon procedure unset_album_favourite_song(i_album_id in songs.album_id%type) is begin update songs s set s.is_album_favourite = 0 where s.favourite_song_of_album_id = i_album_id; end unset_album_favourite_song;
  • 85. © Oren Nakdimon © Oren Nakdimon procedure unset_album_favourite_song(i_album_id in songs.album_id%type) is begin update songs s set s.is_album_favourite = 0 where s.favourite_song_of_album_id = i_album_id; end unset_album_favourite_song; ----------------------------------------------------------- | Id | Operation | Name | ----------------------------------------------------------- | 0 | UPDATE STATEMENT | | | 1 | UPDATE | SONGS | | 2 | TABLE ACCESS BY INDEX ROWID| SONGS | |* 3 | INDEX UNIQUE SCAN | SONGS_FAVOURITE_UK | ----------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("S"."FAVOURITE_SONG_OF_ALBUM_ID"=TO_NUMBER(:I_ALBUM_ID))
  • 86. © Oren Nakdimon © Oren Nakdimon procedure unset_album_favourite_song(i_album_id in songs.album_id%type) is begin update songs s set s.is_album_favourite = 0 where s.favourite_song_of_album_id = i_album_id; end unset_album_favourite_song; ----------------------------------------------------------- | Id | Operation | Name | ----------------------------------------------------------- | 0 | UPDATE STATEMENT | | | 1 | UPDATE | SONGS | | 2 | TABLE ACCESS BY INDEX ROWID| SONGS | |* 3 | INDEX UNIQUE SCAN | SONGS_FAVOURITE_UK | ----------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("S"."FAVOURITE_SONG_OF_ALBUM_ID"=TO_NUMBER(:I_ALBUM_ID))
  • 87. © Oren Nakdimon © Oren Nakdimon procedure unset_album_favourite_song(i_album_id in songs.album_id%type) is begin update songs s set s.is_album_favourite = 0 where s.favourite_song_of_album_id = i_album_id; end unset_album_favourite_song; procedure set_album_favourite_song ( i_album_id in songs.album_id%type, i_track# in songs.track#%type ) is begin unset_album_favourite_song(i_album_id => i_album_id); update songs s set s.is_album_favourite = 1 where s.album_id = i_album_id and s.track# = i_track#; end set_album_favourite_song;
  • 88. © Oren Nakdimon © Oren Nakdimon procedure unset_album_favourite_song(i_album_id in songs.album_id%type) is begin update songs s set s.is_album_favourite = 0 where s.favourite_song_of_album_id = i_album_id; end unset_album_favourite_song; procedure set_album_favourite_song ( i_album_id in songs.album_id%type, i_track# in songs.track#%type ) is begin unset_album_favourite_song(i_album_id => i_album_id); update songs s set s.is_album_favourite = 1 where s.album_id = i_album_id and s.track# = i_track#; end set_album_favourite_song;
  • 89. © Oren Nakdimon © Oren Nakdimon procedure unset_album_favourite_song(i_album_id in songs.album_id%type) is begin update songs s set s.is_album_favourite = 0 where s.favourite_song_of_album_id = i_album_id; end unset_album_favourite_song; procedure set_album_favourite_song ( i_album_id in songs.album_id%type, i_track# in songs.track#%type ) is begin unset_album_favourite_song(i_album_id => i_album_id); update songs s set s.is_album_favourite = 1 where s.album_id = i_album_id and s.track# = i_track#; end set_album_favourite_song;
  • 91. © Oren Nakdimon © Oren Nakdimon Return albums – and all their songs – based on various inputs
  • 92. © Oren Nakdimon © Oren Nakdimon Return albums – and all their songs – based on various inputs procedure get_albums ( i_artist_name in artists.name%type default null, i_genre_id_list in int_tt default null, i_released_after in albums.release_date%type default null, i_released_before in albums.release_date%type default null, o_albums_rc out sys_refcursor, o_songs_rc out sys_refcursor );
  • 93. © Oren Nakdimon © Oren Nakdimon Return albums – and all their songs – based on various inputs procedure get_albums ( i_artist_name in artists.name%type default null, i_genre_id_list in int_tt default null, i_released_after in albums.release_date%type default null, i_released_before in albums.release_date%type default null, o_albums_rc out sys_refcursor, o_songs_rc out sys_refcursor ); create type int_tt as table of integer
  • 94. © Oren Nakdimon © Oren Nakdimon Return albums – and all their songs – based on various inputs procedure get_albums ( i_artist_name in artists.name%type default null, i_genre_id_list in int_tt default null, i_released_after in albums.release_date%type default null, i_released_before in albums.release_date%type default null, o_albums_rc out sys_refcursor, o_songs_rc out sys_refcursor );
  • 95. © Oren Nakdimon © Oren Nakdimon Return albums – and all their songs – based on various inputs procedure get_albums ( i_artist_name in artists.name%type default null, i_genre_id_list in int_tt default null, i_released_after in albums.release_date%type default null, i_released_before in albums.release_date%type default null, o_albums_rc out sys_refcursor, o_songs_rc out sys_refcursor );
  • 96. procedure get_albums ( i_artist_name in artists.name%type default null, i_genre_id_list in int_tt default null, i_released_after in albums.release_date%type default null, i_released_before in albums.release_date%type default null, o_albums_rc out sys_refcursor, o_songs_rc out sys_refcursor) is begin open o_albums_rc for select al.id, al.title, al.release_date, al.genre_id from albums al where ...................... order by al.id; open o_songs_rc for select s.album_id, s.track#, s.title, s.artist_id, s.is_album_favourite from albums al, songs s where ...................... and s.album_id = al.id order by s.album_id, s.track#; end get_albums;
  • 97. © Oren Nakdimon © Oren Nakdimon procedure get_albums ( i_artist_name in artists.name%type default null, i_genre_id_list in int_tt default null, i_released_after in albums.release_date%type default null, i_released_before in albums.release_date%type default null, o_albums_rc out sys_refcursor, o_songs_rc out sys_refcursor) is pragma autonomous_transaction; l_album_id_list int_tt; begin set transaction read only; select al.id bulk collect into l_album_id_list from albums al where (i_artist_name is null or exists (select null from songs s, artists ar where s.album_id = al.id and instr(ar.name, i_artist_name) > 0 and s.artist_id = ar.id)) and (i_genre_id_list is null or al.genre_id member of i_genre_id_list) and al.release_date >= nvl(i_released_after, date '0001-01-01') and al.release_date <= nvl(i_released_before, date '9999-12-31');
  • 98. © Oren Nakdimon © Oren Nakdimon procedure get_albums ( i_artist_name in artists.name%type default null, i_genre_id_list in int_tt default null, i_released_after in albums.release_date%type default null, i_released_before in albums.release_date%type default null, o_albums_rc out sys_refcursor, o_songs_rc out sys_refcursor) is pragma autonomous_transaction; l_album_id_list int_tt; begin set transaction read only; select al.id bulk collect into l_album_id_list from albums al where (i_artist_name is null or exists (select null from songs s, artists ar where s.album_id = al.id and instr(ar.name, i_artist_name) > 0 and s.artist_id = ar.id)) and (i_genre_id_list is null or al.genre_id member of i_genre_id_list) and al.release_date >= nvl(i_released_after, date '0001-01-01') and al.release_date <= nvl(i_released_before, date '9999-12-31');
  • 99. © Oren Nakdimon © Oren Nakdimon procedure get_albums ( i_artist_name in artists.name%type default null, i_genre_id_list in int_tt default null, i_released_after in albums.release_date%type default null, i_released_before in albums.release_date%type default null, o_albums_rc out sys_refcursor, o_songs_rc out sys_refcursor) is pragma autonomous_transaction; l_album_id_list int_tt; begin set transaction read only; select al.id bulk collect into l_album_id_list from albums al where (i_artist_name is null or exists (select null from songs s, artists ar where s.album_id = al.id and instr(ar.name, i_artist_name) > 0 and s.artist_id = ar.id)) and (i_genre_id_list is null or al.genre_id member of i_genre_id_list) and al.release_date >= nvl(i_released_after, date '0001-01-01') and al.release_date <= nvl(i_released_before, date '9999-12-31'); open o_albums_rc for select a.id, a.title, a.release_date, a.genre_id from table(l_album_id_list) c, albums a where a.id = c.column_value order by a.id; open o_songs_rc for select s.album_id, s.track#, s.title, s.artist_id, s.is_album_favourite from table(l_album_id_list) c, songs s where s.album_id = c.column_value order by s.album_id, s.track#; commit; end get_albums;
  • 100. © Oren Nakdimon © Oren Nakdimon procedure get_albums ( i_artist_name in artists.name%type default null, i_genre_id_list in int_tt default null, i_released_after in albums.release_date%type default null, i_released_before in albums.release_date%type default null, o_albums_rc out sys_refcursor, o_songs_rc out sys_refcursor) is pragma autonomous_transaction; l_album_id_list int_tt; begin set transaction read only; select al.id bulk collect into l_album_id_list from albums al where (i_artist_name is null or exists (select null from songs s, artists ar where s.album_id = al.id and instr(ar.name, i_artist_name) > 0 and s.artist_id = ar.id)) and (i_genre_id_list is null or al.genre_id member of i_genre_id_list) and al.release_date >= nvl(i_released_after, date '0001-01-01') and al.release_date <= nvl(i_released_before, date '9999-12-31'); open o_albums_rc for select a.id, a.title, a.release_date, a.genre_id from table(l_album_id_list) c, albums a where a.id = c.column_value order by a.id; open o_songs_rc for select s.album_id, s.track#, s.title, s.artist_id, s.is_album_favourite from table(l_album_id_list) c, songs s where s.album_id = c.column_value order by s.album_id, s.track#; commit; end get_albums;
  • 102. © Oren Nakdimon © Oren Nakdimon Implement an arc relationship from Images to Artists and Albums Implement an arc relationship from a generic entity
  • 103. © 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
  • 104. © 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
  • 105. © 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)
  • 106. © 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
  • 107. © 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
  • 108. © Oren Nakdimon © Oren Nakdimon The Suggested Solution – Option #4  The generic table solution (option #3)  + virtual columns to enforce referential integrity
  • 109. © 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 );
  • 110. © 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 );
  • 111. © 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 );
  • 112. © 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 );
  • 113. © 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 );
  • 114. © 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 );
  • 115. © 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 );
  • 116. © 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;
  • 117. © 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;
  • 118. © 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;
  • 119. © 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;
  • 120. © 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;
  • 121. © 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;
  • 122. © 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;