1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
|
drop database if exists demo;
create database demo;
use demo;
create table ab_physical_person (
person_id integer,
first_name VARCHAR(50),
middle_initial CHAR,
last_name VARCHAR(50),
primary key (person_id));
create table ab_moral_person (
company_id integer,
name VARCHAR(100),
primary key (company_id));
create table in_inventory (
item_id integer,
descr VARCHAR(50),
stock integer,
primary key (item_id));
create table po_order (
po_id integer auto_increment,
cust_type char, /* arc relationship, see cust_id */
cust_id integer, /* FK to ab_physical_person *OR* ab_moral_person */
primary key (po_id));
create table po_order_line (
po_id integer, /* FK to po_order.po_id */
line_no integer,
item_id integer, /* FK to in_inventory.item_id */
qty integer);
#
# Schema integrity enforcement
#
create procedure check_pk_person(in person_type char, in id integer)
begin
declare x integer;
declare msg varchar(128);
/*
Test integrity constraints for an 'arc' relationship.
Based on 'person_type', 'id' points to either a
physical person, or a moral person.
*/
case person_type
when 'P' then
begin
select count(person_id) from ab_physical_person
where ab_physical_person.person_id = id
into x;
if (x != 1)
then
set msg= concat('No such physical person, PK:', id);
SIGNAL SQLSTATE '45000' SET
MESSAGE_TEXT = msg,
MYSQL_ERRNO = 10000;
end if;
end;
when 'M' then
begin
select count(company_id) from ab_moral_person
where ab_moral_person.company_id = id
into x;
if (x != 1)
then
set msg= concat('No such moral person, PK:', id);
SIGNAL SQLSTATE '45000' SET
MESSAGE_TEXT = msg,
MYSQL_ERRNO = 10000;
end if;
end;
else
begin
set msg= concat('No such person type:', person_type);
SIGNAL SQLSTATE '45000' SET
MESSAGE_TEXT = msg,
MYSQL_ERRNO = 20000;
end;
end case;
end
$$
create procedure check_pk_inventory(in id integer)
begin
declare x integer;
declare msg varchar(128);
select count(item_id) from in_inventory
where in_inventory.item_id = id
into x;
if (x != 1)
then
set msg= concat('Failed integrity constraint, table in_inventory, PK:',
id);
SIGNAL SQLSTATE '45000' SET
MESSAGE_TEXT = msg,
MYSQL_ERRNO = 10000;
end if;
end
$$
create procedure check_pk_order(in id integer)
begin
declare x integer;
declare msg varchar(128);
select count(po_id) from po_order
where po_order.po_id = id
into x;
if (x != 1)
then
set msg= concat('Failed integrity constraint, table po_order, PK:', id);
SIGNAL SQLSTATE '45000' SET
MESSAGE_TEXT = msg,
MYSQL_ERRNO = 10000;
end if;
end
$$
create trigger po_order_bi before insert on po_order
for each row
begin
call check_pk_person(NEW.cust_type, NEW.cust_id);
end
$$
create trigger po_order_bu before update on po_order
for each row
begin
call check_pk_person(NEW.cust_type, NEW.cust_id);
end
$$
create trigger po_order_line_bi before insert on po_order_line
for each row
begin
call check_pk_order(NEW.po_id);
call check_pk_inventory(NEW.item_id);
end
$$
create trigger po_order_line_bu before update on po_order_line
for each row
begin
call check_pk_order(NEW.po_id);
call check_pk_inventory(NEW.item_id);
end
$$
#
# Application helpers
#
create procedure po_create_order(
in p_cust_type char,
in p_cust_id integer,
out id integer)
begin
insert into po_order set cust_type = p_cust_type, cust_id = p_cust_id;
set id = last_insert_id();
end
$$
create procedure po_add_order_line(
in po integer,
in line integer,
in item integer,
in q integer)
begin
insert into po_order_line set
po_id = po, line_no = line, item_id = item, qty = q;
end
$$
#
# Create sample data
#
insert into ab_physical_person values
( 1, "John", "A", "Doe"),
( 2, "Marry", "B", "Smith")
;
insert into ab_moral_person values
( 3, "ACME real estate, INC"),
( 4, "Local school")
;
insert into in_inventory values
( 100, "Table, dinner", 5),
( 101, "Chair", 20),
( 200, "Table, coffee", 3),
( 300, "School table", 25),
( 301, "School chairs", 50)
;
select * from ab_physical_person order by person_id;
person_id first_name middle_initial last_name
1 John A Doe
2 Marry B Smith
select * from ab_moral_person order by company_id;
company_id name
3 ACME real estate, INC
4 Local school
select * from in_inventory order by item_id;
item_id descr stock
100 Table, dinner 5
101 Chair 20
200 Table, coffee 3
300 School table 25
301 School chairs 50
#
# Entering an order
#
set @my_po = 0;
/* John Doe wants 1 table and 4 chairs */
call po_create_order("P", 1, @my_po);
call po_add_order_line (@my_po, 1, 100, 1);
call po_add_order_line (@my_po, 2, 101, 4);
/* Marry Smith wants a coffee table */
call po_create_order("P", 2, @my_po);
call po_add_order_line (@my_po, 1, 200, 1);
#
# Entering bad data in an order
#
call po_add_order_line (@my_po, 1, 999, 1);
ERROR 45000: Failed integrity constraint, table in_inventory, PK:999
#
# Entering bad data in an unknown order
#
call po_add_order_line (99, 1, 100, 1);
ERROR 45000: Failed integrity constraint, table po_order, PK:99
#
# Entering an order for an unknown company
#
call po_create_order("M", 7, @my_po);
ERROR 45000: No such moral person, PK:7
#
# Entering an order for an unknown person type
#
call po_create_order("X", 1, @my_po);
ERROR 45000: No such person type:X
/* The local school wants 10 class tables and 20 chairs */
call po_create_order("M", 4, @my_po);
call po_add_order_line (@my_po, 1, 300, 10);
call po_add_order_line (@my_po, 2, 301, 20);
select * from po_order;
po_id cust_type cust_id
1 P 1
2 P 2
3 M 4
select * from po_order_line;
po_id line_no item_id qty
1 1 100 1
1 2 101 4
2 1 200 1
3 1 300 10
3 2 301 20
select po_id as "PO#",
( case cust_type
when "P" then concat (pp.first_name,
" ",
pp.middle_initial,
" ",
pp.last_name)
when "M" then mp.name
end ) as "Sold to"
from po_order po
left join ab_physical_person pp on po.cust_id = pp.person_id
left join ab_moral_person mp on po.cust_id = company_id
;
PO# Sold to
1 John A Doe
2 Marry B Smith
3 Local school
select po_id as "PO#",
ol.line_no as "Line",
ol.item_id as "Item",
inv.descr as "Description",
ol.qty as "Quantity"
from po_order_line ol, in_inventory inv
where inv.item_id = ol.item_id
order by ol.item_id, ol.line_no;
PO# Line Item Description Quantity
1 1 100 Table, dinner 1
1 2 101 Chair 4
2 1 200 Table, coffee 1
3 1 300 School table 10
3 2 301 School chairs 20
drop database demo;
|