Upload
eancrimicri
View
316
Download
28
Embed Size (px)
Citation preview
1
DATABASES LAB NOTES
Christian Mancas
Assoc. Prof. Dr., Mathematics and Computer Science Dept., Ovidius
University Constanta and
Engineering in Foreign Languages Dept., Bucharest Polytechnic
University
Database Architect, Asentinel Intl. srl, Bucharest
Alina Iuliana Dicu
Drd., Engineering in Foreign Languages Dept., Bucharest Polytechnic
University
Business Intelligence Consultant, VIEW srl, Bucharest
Bucharest Polytechnic University Press
2014
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
2
Contents Volume I: Data definition and manipulation in SQL ....................................... 12
Chapter 1. 1st Lab: DDL statements in SQL and GUI ........................................... 13
1.1 Introduction .............................................................................................. 13
1.2 Exercises ................................................................................................... 13
1.2.1 Creating and opening an Access database ........................................ 15
1.2.2 Creating tables in Access with Design View (QBE) ............................ 15
1.2.3 Creating tables in Access with SQL DDL statements ......................... 37
1.2.4 Creating and opening an Oracle database ........................................ 39
1.2.5 Creating tables in Oracle with the SQL Developer GUI ...................... 51
1.2.6 Creating tables in Oracle with SQL DDL statements.......................... 68
1.3 Best practice rules .............................................................................. 74
1.4 Homework .......................................................................................... 75
Chapter 2. 2nd Lab: Transactional DML statements in SQL and GUI ................... 76
2.1 Transactional SQL DML statements.......................................................... 76
2.2 Exercises ............................................................................................. 77
2.2.1 Prerequisites in Access ...................................................................... 77
2.2.2 Exercises in Access ............................................................................. 84
2.2.3 Prerequisites in Oracle ...................................................................... 95
2.2.4 Exercises in Oracle ........................................................................... 102
2.3 Best practice rules .................................................................................. 117
2.4 Homework ........................................................................................ 118
Chapter 3. 3rd Lab: INNER JOIN, GROUP BY, and HAVING clauses ................... 119
3.1 Introduction ...................................................................................... 119
3.2 The algorithm for assisting DML statements design ........................ 120
3.3 Exercises in Access ............................................................................ 122
3.4 Exercises in Oracle ............................................................................ 135
3.5 Best practice rules ............................................................................ 156
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
3
3.5 Homework ........................................................................................ 157
Chapter 4. 4th Lab: Outer Joins, the Is Null predicate, and UNIONs ................. 158
4.1 Prerequisites in Access ........................................................................... 158
4.1.1 Adding needed countries to the COUNTRIES table ......................... 158
4.1.2 Adding the NEIGHBORS table and populating its instance ............. 158
4.2 Exercises in Access .................................................................................. 160
4.3 Prerequisites in Oracle ........................................................................... 170
4.1.1 Adding needed countries to the COUNTRIES table ......................... 170
4.1.2 Adding the NEIGHBORS table and populating its instance ............. 171
4.4 Exercises in Oracle .................................................................................. 173
4.5 Best practice rules .................................................................................. 187
4.6 Homework .............................................................................................. 187
Chapter 5. 5th Lab: Self-Joins and Transitive Closures ...................................... 189
5.1 Exercises in Access .................................................................................. 189
5.2 Exercises in Oracle .................................................................................. 206
5.3 Best practice rules .................................................................................. 225
5.4 Homework .............................................................................................. 225
Chapter 6. 6th Lab: First test (SQL) .................................................................... 228
6.1 Number 1 ................................................................................................ 228
6.1.1 Access solution ................................................................................ 229
6.1.2 Oracle solution ................................................................................ 232
6.2 Number 2 ................................................................................................ 236
6.2.1 Access solution ................................................................................ 238
6.2.2 Oracle solution ................................................................................ 241
6.3 Best practice rules .................................................................................. 244
6.4 Homework .............................................................................................. 244
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
4
Volume II: Database design ....................................................................... 246
Chapter 7. 7th Lab: Business Analysis ............................................................... 247
7.1 Exercises ................................................................................................. 247
7.2 Best practice rules .................................................................................. 267
7.3 Homework .............................................................................................. 267
Chapter 8. 8th Lab: Elementary mathematic db schemes ................................ 269
8.1 Exercises ................................................................................................. 269
8.2 Best practice rules .................................................................................. 281
8.3 Homework .............................................................................................. 281
Chapter 9. 9th Lab: Analyzing E-RD cycles ......................................................... 282
9.1 Exercises ................................................................................................. 282
9.2 Best practice rules .................................................................................. 290
9.3 Homework .............................................................................................. 291
Chapter 10. 10th Lab: Enforcing non-relational constraints ............................. 292
10.1 Prerequisites in Access: Forms and Event-Driven Methods ................. 292
10.1.1 Forms/reports GUI: controls and properties ................................. 295
10.1.2 Forms/reports VBA classes: event-driven and ordinary methods 303
10.1.3 VBA programming techniques and tips ......................................... 304
10.1.4 VBA and SQL Functions .................................................................. 312
10.2 Exercises in Access ................................................................................ 316
10.3 Prerequisites in Oracle: Triggers .......................................................... 321
10.4 Exercises in Oracle ................................................................................ 321
10.5 Best practice rules ................................................................................ 321
10.6 Homework ............................................................................................ 321
Chapter 11. 11th Lab: Reporting in Access & 2nd DB test training ..................... 322
11.1 Reporting in Access............................................................................... 322
11.1.1 Prerequisites .................................................................................. 322
11.1.2 Exercises ........................................................................................ 338
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
5
11.2 2nd DB test training ............................................................................... 346
11.2.1 Access implementation ..................................................................... 354
11.2.2 Oracle implementation ..................................................................... 362
11.3 Best practice rules ................................................................................ 362
11.4 Homework ............................................................................................ 362
Chapter 12. 12th Lab: Second Test (DB Design & Implementation) ................. 364
12.1 Subject 1 ............................................................................................... 364
12.2.1 Access implementation ..................................................................... 369
12.1.2 Oracle implementation ..................................................................... 373
12.2 Subject 2 ............................................................................................... 373
12.2.1 Access implementation ..................................................................... 373
12.2.2 Oracle implementation ..................................................................... 373
Chapter 13. 13th Lab: Test Redoing (SQL or DB Design & Implementation) ..... 374
13.1 SQL test redoing ................................................................................... 374
6.1.1 Access solution ................................................................................ 375
6.1.2 Oracle solution ................................................................................ 377
13.2 DB design and implementation test redoing ....................................... 382
13.2.1 Access implementation ..................................................................... 387
13.2.2 Oracle implementation ..................................................................... 391
Chapter 14. 14th Lab: Projects Defense ............................................................ 392
Conclusion ........................................................................................................ 393
References ........................................................................................................ 394
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
6
Appendix 1: DB Project Example ...................................................................... 395
Design, implementation, and usage of a Library db......................................... 395
Table of Contents ............................................................................................. 396
1. Business Analysis ...................................................................................... 397
1.0 Description of the sub-universe of discourse ................................... 397
1.1 Entity-Relationship Diagrams ........................................................... 398
1.2 Associated Restrictions List .............................................................. 399
2. Mathematical Scheme .............................................................................. 403
2.0 Initial Mathematical Scheme ............................................................ 403
2.1 First refinement algorithm: Sets, Functions, and Constraints Design
Assistance ..................................................................................................... 404
2.1.1 Sets .................................................................................................. 404
2.1.2 Functions ......................................................................................... 405
2.1.3 Constraints....................................................................................... 409
2.2 Second refinement algorithm: Keys Discovery Assistance ............... 410
2.3 Third refinement algorithm: E-RD Cycles Analysis ........................... 413
2.3.1 First commutative-type cycle .......................................................... 414
2.3.2 First generalized commutative-type cycle ....................................... 414
2.3.3 Second generalized commutative-type cycle .................................. 416
2.3.4 Second commutative-type cycle ...................................................... 417
2.3.5 Third generalized commutative-type cycle .............................. 417
2.3.6 Third commutative-type cycle ......................................................... 418
2.4 Final Mathematical Scheme ............................................................. 419
3. Relational Scheme and Associated Non-relational Constraints List ......... 422
3.0 Relational db scheme ....................................................................... 422
3.1 Non-relational constraints list .......................................................... 425
4. Database Implementation ........................................................................ 426
4.0 Technology choice ............................................................................ 426
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
7
4.1 Access db .......................................................................................... 427
4.1.0 DDL statements for creating and populating the db ................ 427
4.1.1 Tables Relationships ................................................................. 429
4.1.2 Table Lookups SQL statements ................................................. 429
4.1.3 Validation rules, Comments, and Default values .................... 432
4.2 Oracle db .......................................................................................... 437
5. Non-relational constraints enforcement .................................................. 437
5.1 Access solutions ................................................................................ 437
5.1.1 Edition : VOLUMES EDITIONS, onto ............................................ 437
5.1.2 CP6 ................................................................................................... 444
5.1.3 CVC4 ................................................................................................ 444
5.1.4 CVC5 ................................................................................................ 444
5.1.5 CBL5 ................................................................................................. 444
5.1.6 CBL6 ................................................................................................. 445
5.1.7 CBL7 ................................................................................................. 447
5.1.8 ACC1 ................................................................................................ 448
5.2 Oracle solutions ...................................................................................... 449
6. Database Usage ........................................................................................ 449
6.1 Access Queries and Reports ............................................................. 449
6.1.1 Queries ............................................................................................ 449
6.1.2 Reports ............................................................................................ 453
6.2 Oracle Views and Stored Procedures ............................................... 456
7. Conclusion ................................................................................................ 456
8. Bibliography .............................................................................................. 457
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
8
Appendix 2: VBA and SQL functions by categories .......................................... 458
A2.1 String processing functions .................................................................. 458
A2.1.1 LCase ............................................................................................. 458
A2.1.2 UCase ............................................................................................ 458
A2.1.3 Len ................................................................................................. 458
A2.1.4 Left and Right ................................................................................ 458
A2.1.5 LTrim, RTrim, and Trim .................................................................. 459
A2.1.6 StrReverse ..................................................................................... 459
A2.1.7 InStr ............................................................................................... 459
A2.1.8 InStrB ............................................................................................. 460
A2.1.9 InstrRev ......................................................................................... 460
A2.1.10 Replace ........................................................................................ 461
A2.1.11 StrComp ....................................................................................... 462
A2.1.12 Space ........................................................................................... 462
A2.1.13 String ........................................................................................... 462
A2.1.14 StrConv ........................................................................................ 463
A2.1.15 Join .............................................................................................. 464
A2.1.16 Split.............................................................................................. 465
A2.2 Data types associated functions .......................................................... 465
A2.2.1 IsNull .............................................................................................. 465
A2.2.2 IsEmpty .......................................................................................... 466
A2.2.3 TypeName ..................................................................................... 466
A2.2.4 VarType ......................................................................................... 467
A2.2.5 IsDate ............................................................................................ 468
A2.2.6 IsNumeric ...................................................................................... 468
A2.2.7 IsArray ........................................................................................... 468
A2.2.8 IsMissing ........................................................................................ 468
A2.2.9 IsObject ......................................................................................... 469
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
9
A2.2.10 IsError .......................................................................................... 469
A2.3 Date functions ...................................................................................... 469
A2.3.1 Date and Date$ ............................................................................. 469
A2.3.2 Time and Time$ ............................................................................. 469
A2.3.3 Now ............................................................................................... 469
A2.3.4 DatePart ........................................................................................ 470
A2.3.5 DateSerial ...................................................................................... 472
A2.3.6 TimeSerial ...................................................................................... 473
A2.3.7 DateValue ...................................................................................... 473
A2.3.8 TimeValue ..................................................................................... 474
A2.3.9 DateAdd ........................................................................................ 474
A2.3.10 DateDiff ....................................................................................... 475
A2.3.11 Day .............................................................................................. 476
A2.3.12 Weekday ..................................................................................... 477
A2.3.13 WeekdayName ............................................................................ 477
A2.3.14 Month .......................................................................................... 477
A2.3.15 MonthName ................................................................................ 478
A2.3.16 Year ............................................................................................. 478
A2.3.17 Hour ............................................................................................. 478
A2.3.18 Minute ......................................................................................... 478
A2.3.19 Second ......................................................................................... 478
A2.4 Numeric functions ................................................................................ 479
A2.4.1 Abs ................................................................................................. 479
A2.4.2 Int and Fix ...................................................................................... 479
A2.4.3 Partition ......................................................................................... 479
A2.4.4 Round ............................................................................................ 480
A2.4.5 Sgn ................................................................................................. 480
A2.4.6 Rnd ................................................................................................ 481
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
10
A2.4.7 Trigonometric and other algebraic functions ............................... 482
A2.4.8 Derived Math Functions ................................................................ 482
A2.5 Conversion functions ........................................................................... 484
A2.5.1 Str .................................................................................................. 486
A2.5.2 Val .................................................................................................. 486
A2.5.3 Format ........................................................................................... 487
A2.5.4 FormatCurrency ............................................................................ 497
A2.5.5 FormatDateTime ........................................................................... 498
A2.5.6 FormatNumber .............................................................................. 499
A2.5.7 FormatPercent .............................................................................. 499
A2.5.8 Asc, AscB, and AscW ...................................................................... 500
A2.5.9 Chr, ChrB, and ChrW ...................................................................... 500
A2.5.10 Hex .............................................................................................. 500
A2.5.11 Oct ............................................................................................... 501
A2.5.12 CVErr............................................................................................ 501
A2.6 Program flow control functions ........................................................... 502
A2.6.1 IIf ................................................................................................... 502
A2.6.2 Switch ............................................................................................ 502
A2.6.3 Choose ........................................................................................... 503
A2.6.4 CallByName ................................................................................... 503
A2.6.5 DoEvents ....................................................................................... 504
A2.7 File functions ........................................................................................ 505
A2.7.1 FileAttr ........................................................................................... 505
A2.7.2 FileDateTime ................................................................................. 505
A2.7.3 FileLen ........................................................................................... 505
A2.7.4 LOF ................................................................................................ 506
A2.7.5 FreeFile .......................................................................................... 506
A2.7.6 Input .............................................................................................. 506
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
11
A2.7.7 Loc ................................................................................................. 507
A2.7.8 Seek ............................................................................................... 507
A2.7.9 EOF ................................................................................................ 507
A2.8 Other operating system functions ....................................................... 508
A2.8.1 Shell ............................................................................................... 508
A2.8.2 CurDir ............................................................................................ 509
A2.8.3 Dir .................................................................................................. 509
A2.8.4 GetAttr .......................................................................................... 511
A2.8.5 Environ .......................................................................................... 511
A2.8.6 GetAllSettings ................................................................................ 512
A2.8.7 GetSetting ..................................................................................... 512
A2.8.8 Spc ................................................................................................. 513
A2.8.9 Tab ................................................................................................. 513
A2.9 Array functions ..................................................................................... 514
A2.9.1 Array .............................................................................................. 514
A2.9.2 Filter .............................................................................................. 515
A2.9.3 LBound .......................................................................................... 516
A2.9.4 UBound.......................................................................................... 516
A2.10 Miscellanea functions ........................................................................ 517
A2.10.1 Error ............................................................................................ 517
A2.10.2 QBColor ....................................................................................... 517
A2.10.3 RGB .............................................................................................. 518
A2.10.4 CreateObject ............................................................................... 519
A2.10.5 GetObject .................................................................................... 520
A2.10.6 Financial functions ...................................................................... 522
Subject Index .................................................................................................... 523
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
12
Volume I: Data
definition and
manipulation in SQL
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
13
Chapter 1. 1st Lab: DDL statements in SQL and GUI
1.1 Introduction SQL stands for Structured Query Language.
There are 2 SQL standards:
1. The pure one (based on first order logic and union), called ANSI
92
2. The extended one, including recursiveness and object-oriented
(nested tables), called ANSI 99
This semester we will study only pure SQL.
SQL is divided into 2 sublanguages:
1. DDL (Data Definition Language) – it is including all the state-
ments for modifying db structure (e.g. Create, Alter, Drop db/table
/constraint/index/user/group/etc., Grant/Revoke rights, etc.);
Note that, except for DROP (which is, trivially, also deleting cor-
responding data) no other DDL statement is affecting data instan-
ces.
2. DML (Data Manipulation Language) – used to retrieve and
manipulate data, without affecting the database structure (see fol-
lowing labs).
1.2 Exercises P1.1 Create a database (db) for storing data about countries (x, Country
(Name), Capital, Nationality, Population) and cities (x, City (Name),
Country).
Moreover, note that (at this level of refinement) City Country : CITIES
ASCII(128) COUNTRIES is minimally one-to-one (as there may not
be two cities having same name in a same country, which is, by the way,
true for very big cities).
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
14
Solution:
Please first note that neither cities, nor countries are character strings, but
objects! Then, note that between these 2 distinct sets there are 2 functions
(see figure 1.0):
Country : CITIES COUNTRIES (whose graph is storing, for each city,
the country to which it belongs) and
Capital : COUNTRIES CITIES (whose graph is storing, for each coun-
try, the city which is its capital; note that this function is one-to-one, as no
city may simultaneously be the capital of more than one country).
Figure 1.0 Functions between sets COUNTRIES and CITIES
As you will see in your db lectures, sets are implemented via tables and
functions via columns (with structural functions –that is functions taking
values from object sets– via foreign keys).
Obviously, the rest of the functions (one-to-one, when is replaced by
) to be implemented are the following:
x : COUNTRIES NAT(3)
Country : COUNTRIES ASCII(255)
Nationality : COUNTRIES ASCII(255) NULLS
Population : COUNTRIES [100, 2000000000] NULLS
x : CITIES NAT(8)
City : CITIES ASCII(128)
where NAT(n) = {x NAT | x < 10,0000,000 } and ASCII(l) = {x
ASCII* | Length(x) l} are finite subsets of the naturals and, respec-
tively, the freely generated monoid over the ASCII alphabet.
Country
Capital COUNTRIES CITIES
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
15
1.2.1 Creating and opening an Access database
Let start’s now create these tables in MS Access, by using its GUI:
First, please create on D: (the students’ logical drive on the lab’s PCs) a
folder with your group name and your name -> right click in that folder ->
create new Microsoft Access database-> re-name it -> double-click on it
to open it -> click on Enable Content.
1.2.2 Creating tables in Access with Design View (QBE)
QBE stands for Query-by-Example; MS Design View (both in Access and
SQL Server) is just a QBE implementation.
Click on Create -> Table ->Design View.
Type x in the first row of Field Name and choose AutoNumber1 for Data
Type:
Figure 1.0 Defining a surrogate key in Access
1 Note that, in Access, autonumber is a Long (integer), whose values cannot be restricted
to any of its subsets, as, besides its Increment default value (starting by default with 1),
there is also a Random value (which uses both negative and positive integers).
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
16
In the Description column (whose values are displayed by Access in the
task bar when the table instance is opened) type “Primary surrogate
COUNTRIES key” and then click on the Primary Key icon:
Figure 1.1 Defining the surrogate key to be the primary one in Access
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
17
In the second row, type Country, leave the Text default Data Type, type
“Country name” in Description, toggle Required to Yes (for not accepting
nulls) and Allow zero length to No (to also ban dirty nulls – that is not
empty strings only containing non-printable characters), and choose for
Unique Yes (No Duplicates) in order to declare it as a key (as there may
not be two countries having same names):
Figure 1.2 Defining not null texts and single keys constraints in Access
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
18
In the third row, type Nationality, leave the Text default Data Type, type
“Main country's people nationality” in Description, toggle Allow zero
length to No (note that you should always do it for any character strings
stored in any db, even if they are not required), and change Field size to
32:
Figure 1.3 Defining non-dirty null texts in Access
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
19
In the fourth row, type Population, choose the Number Data Type, type
“Country population” in Description, choose Standard for Format and 0
for Decimal Places, type “Between 100 And 2000000000” for Validation
rule and “Country population should be a natural between 100 and
2,000,000,000!” for Validation Text (to implement the corresponding (co)
domain constraint):
Figure 1.4 Defining (co-)domain constraints in Access
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
20
As Capital cannot be fully implemented for the time being (because there
is no table CITIES yet), save and close this table scheme, by giving it the
name COUNTRIES:
Figure 1.5 Saving table schemes in Access
Figure 1.6 Naming table schemes in Access
Right-click the table’s name, click on Table Properties and type “World
countries of interest.” in the Description text box, and then click OK:
Figure 1.7 Associating a description to an Access table
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
21
For entering data into it, just double-click the table name and start typing
corresponding data into its instance:
Figure 1.8 Entering data in Access
Continue entering data at least on Moldavia, U.S.A., and U.K., by
consulting the Internet for their corresponding populations and then close
table. Note that you can enter any ASCII characters (not only the first 128
standard ones), like, for example, the Romanian’s ‘â’, ‘ș’, ‘ț’, etc.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
22
Similarly, create table CITIES with x as primary surrogate key and City;
for Country, chose as Data Type the last option (Lookup Wizard):
Figure 1.9 Implementing foreign keys by using Access’ Lookup Wizard
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
23
In its first screen, leave its default settings and just click on Next:
Figure 1.10 First Access Lookup Wizard screen
Do same thing for the second screen too:
Figure 1.11 Second Access Lookup Wizard screen
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
24
On the third one, choose columns x and Country from COUNTRIES, by
using the “>” button, and then click Next:
Figure 1.12 Third Access Lookup Wizard screen
On the fourth one, choose Country to be shown in ascending order, and
then click Next:
Figure 1.13 Fourth Access Lookup Wizard screen
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
25
On the fifth one, leave all default options as such (note that x’s values will
be hidden and only corresponding Country’s ones will be shown) and
click Next:
Figure 1.14 Fifth Access Lookup Wizard screen
On the sixth one, click on Enable Data Integrity and then Finish:
Figure 1.15 Sixth Access Lookup Wizard screen
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
26
Note that the default Restrict Delete option will prevent users to delete
from COUNTRIES any row which is referred to by at least one row from
CITIES2.
Dually, note that failing to enforce this referential integrity would poten-
tially result in inconsistent (hence, implausible) data instances: for exam-
ple, they might delete the row from Romania, leaving in CITIES “dangl-
ing pointers” (i.e. pointers pointing to inexistent values) for all Romanian
cities.
Next, confirm system’s request to save table, in order for it to save the
corresponding tables’ relationship, by clicking Yes:
Figure 1.16 Access tables relationships saving confirmation screen
Give the name CITIES to the table and click OK:
Figure 1.17 Saving the newly created CITIES table
2 Except for very rare, generally system cases (for users ones always get their prior
written approval for it!), DO NOT USE the Cascade Delete option: its effect is that
when users are deleting a row from COUNTRIES, all corresponding rows from CITIES
will be deleted too Imagine, for example, that Vodafone’s world integrated db would
have all foreign keys declared with the Cascade Delete option: trivially, when deleting
from CONTINENTS the row corresponding to Europe, all Europe’s countries, cities,
customers, invoices, calls, SMSs, etc. would be deleted too!
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
27
Type “Country to which city belongs” in corresponding Description co-
lumn and choose Yes for Required:
Figure 1.18 Declaring Country from CITIES to be totally defined
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
28
Click on the Lookup tab and note that (see figure 1.19 below):
- the corresponding Display Control is a Combo (not a default Text)
Box, whose values are computed by a query (from a table)
- attached to this combo box (or drop-down list), the following SQL
DML SELECT statement is computing its actually shown values:
SELECT [COUNTRIES].[x], [COUNTRIES].[Country]
FROM COUNTRIES ORDER BY [Country];
- the Country Bound Column (i.e. the values which are actually
stored) is the first one (i.e. x) from those computed by the above
SELECT
- the above SELECT computes 2 columns (redundant Column
Count property!)
- when opened, the combo box will not display Column Heads
- the first computed column (x) is hidden (as its Column Width is 0),
so that the second column (Country) corresponding values are
shown instead (having 1 inch width, that you can modify as
needed)
- change List Rows to 32 (in order to increase the maximum number
of rows that are displayed when opening the combo box)
- the total combo box List Width is 0 + 1 = 1 inch (note that you can
give it other values, but this should not ever be the case: if you set
a lesser value, the combo box would be correspondingly truncated
to its right; if you set a greater one, the combo box would be
correspondingly padded with blanks to its right)
- the Limit to List was set to Yes (its default being No!!), for
preventing users to store not existing rows in countries (but only
letting them to choose from existing ones)
- leave default values for all other lookup properties and save your
modification
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
29
Figure 1.19 Country’s lookup actual properties values
In order to enforce the City Country : CITIES ASCII(128)
COUNTRIES minimally one-to-oneness, you should then click on the
Indexes button from the Table Tools tab; the following window will open
(showing the only key of this table up to now, namely the primary one):
Figure 1.20 The Indexes Access window
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
30
Type „citiesKey” in the Index Name column of the second row (any
unique index name in this db would do too), chose City in the correspond-
ing Field Name and Country underneath it (leaving blank the correspond-
ing Index Name: if you type anything in this cell, instead of defining an
index on the product City Country, you would define two single inde-
xes, one on City and the other one on Country!) and then, on the first line
of the newly created index, toggle Unique to Yes (otherwise, this would
remain a simple index meant to speedup searches into this table and slow-
down its inserts/deletes/updates!)3.
Figure 1.21 Defining a product (compound, concatenated) key in Access
Close the Indexes window, save, and close the CITIES table.
3 Note that you would always obtain better results for your product (compound, concate-
nated, etc.) indexes usage, by following this rule: in any position of the index, the current
column should have less distinct values than the previous one and more than the subse-
quent one; this is why here we defined this key as City Country instead of Country
City (which are, mathematically speaking, equivalent).
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
31
Click on the Database Tools tab and then on Relationships: the only
“one-to-many” existing relationship (corresponding to the foreign key
Country from CITIES) is displayed.
Figure 1.22 Access Relationships Db Tool window
Now you can close this window, open CITIES and enter at least data for
Bucharest, Chișinău, Washington, and London:
Figure 1.23 Entering data in Access’ CITIES table instance
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
32
Then, close CITIES and re-open COUNTRIES in design mode for similar-
ly adding Capital to it; after Access saves the corresponding relationship,
its corresponding window looks like:
Figure 1.24 Access Relationships window after adding the foreign key
Capital
First, please note that Access does not show more than one relationship
between any two table instances; as such, it adds a second table instance
(COUNTRIES_1) for COUNTRIES.
Much more important, please note that there is a bug in Access (in fact, a
wrong feature: they confuse one-to-one functions with the bijective ones):
if you first declare a column unique and then a foreign key, then it either
adds a corresponding 1-to-1 relationship instead (if the two involved
tables instances are equipotent), or rejects any attempt to enforce cor-
responding referential integrity (if the two instances are not equipotent):
Figure 1.25 Access error message when trying to enforce referential
integrities on non-equipotent 1-to-1 relationships
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
33
When you save COUNTRIES, switch it to the default Datasheet View and
start to enter capitals data, please note that, although in this very
particular case no confusion is possible (at least for Romanians),
generally, city names are not enough to uniquely identify them (recall that
CITIES semantic key is City Country):
Figure 1.26 Updating capitals data by using an Access combo box
To make the Capital combo box display the corresponding countries too,
you have to modify its Row Source property accordingly: switch COUN-
TRIES to design mode, go to Capital’s Lookup tab on the Row Source
property line and click on the “…” button to its right:
Figure 1.27 Modifying an Access combo box Row Source property
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
34
The following QBE (Query Design Mode) window opens:
Figure 1.28 An Access QBE (Query Design Mode) window
In order to concatenate to city names their corresponding country ones,
click on Query Type, then on Show Table, choose COUNTRIES, click on
Add, and then on Close:
Figure 1.29 Adding new tables to a query in Access QBE
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
35
By default, Access not only adds the corresponding table to the query, but
also all relationships between them (in this case, both ones from figure
1.24 above):
Figure 1.30 Access QBE with all relationships between two tables
As here we do not need the relationship corresponding to Capital, right-
click on it and then click on Delete (note that it will be deleted only from
this query, not from the db):
Figure 1.31 Access QBE with only the needed relationship in this case
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
36
Next, in the second column of the first grid row (Field), type after City
the needed suffix so that, in the end, it reads: City, Country: City & “, “ &
COUNTRIES.Country (capitalization doesn’t matter), where the prefix
“City Country” is the label of this column (ended by ‘:’), & is the string
concatenation operator, and prefixing the name of the column Country
with its table name COUNTRIES (separated by a dot) is necessary in or-
der to eliminate ambiguity (as there is also a column Country in CITIES).
If you click on View and then on SQL View, you can see the equivalent
SQL statement (see figure 1.32):
SELECT CITIES.x, [City] & ", " &
[COUNTRIES].[Country] AS [City, Country]
FROM COUNTRIES INNER JOIN CITIES
ON COUNTRIES.x = CITIES.Country
ORDER BY [City] & ", " & [COUNTRIES].[Country];
Figure 1.32 Equivalent Access SQL statement for the Capital combo box
Save and close the query, then toggle Column Heads to Yes, increase both
the second Column and the List Widths to 2”, save the table scheme and
switch it to the normal Datasheet View.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
37
Now, when you open the Capital combo box, both city and corresponding
country names are displayed (separated by a comma and a space):
Figure 1.33 Updating capitals data by using an Access combo box made
out of two concatenated columns (compare with figure 1.26)
1.2.3 Creating tables in Access with SQL DDL statements
The following Access DDL SQL statements are creating absolutely similar
tables:
CREATE TABLE COUNTRIES (x Counter PRIMARY KEY,
Country varchar(255) NOT NULL UNIQUE, Capital
Long, Nationality varchar(32) NOT NULL,
Population Long);4
CREATE TABLE CITIES (x Counter Primary Key,
Country Long NOT NULL, city varchar(255) NOT
NULL, Population Long, CONSTRAINT citiesKey
UNIQUE (Country, City), CONSTRAINT fkContry
FOREIGN KEY (Country) REFERENCES Countries);
ALTER TABLE COUNTRIES ADD CONSTRAINT fkCapital
FOREIGN KEY (Capital) REFERENCES CITIES1;
4 Note that CHECK constraints are allowed only programmatically in Access, via VBA
and ADO or DAO, so that you cannot restrict only through SQL Population values.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
38
In order to create and run such DDL queries in Access, you have to create
a new query by clicking Query Design from the Create tab, then click
Close on the Show table modal popup window (see figure 1.29 above),
without adding any table, then click on View, then on SQL View and then
replace the “SELECT;” statement prefix (see figure 1.34 below) with a
desired DDL statement (see figure 1.35 below), then save, name, close it,
and finally double-click its name for running it.
Figure 1.34 The SQL view of an initially empty Access QBE query
Figure 1.35 The SQL view of an Access DDL SQL query
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
39
Note that, when created through SQL, there is no issue whatsoever with
the referential integrity for unique foreign keys, not even in Access.
Unfortunately, when created through SQL, no lookups, check constraints,
or descriptions are possible: you have to add them afterwards, through the
Access GUI, just as explained above.
1.2.4 Creating and opening an Oracle database
Confusingly, Oracle uses the term “user” instead of “database”; you can
create a user through its web Enterprise Manager Console, if logged in as
SYSDBA:
Figure 1.36 Logging in as SYSDBA in Oracle’s Enterprise Manager
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
40
Successful login opens the Enterprise Manager Home page:
Figure 1.37 Oracle Enterprise Manager Home page
Click on the Server tab and then on the Users option of the Security
group:
Figure 1.38 Oracle Enterprise Manager Server page
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
41
In the Users page, click on the Create button:
Figure 1.39 Oracle Enterprise Manager Users page
Fill in requested data in order to create a new user (that you have to
unlock and not to let Oracle expire its password immediately) :
Figure 1.40 Oracle Enterprise Manager Create User page
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
42
Do not create users in system’s tablespaces: click on the flashlight icon to
the right of the Default Tablespace text box and either choose the Users
one (see figure 1.41 below) or a previously created other user tablespace.
Figure 1.41 Oracle Enterprise Manager Select Tablespace page
Click on the USERS radio button, then on Select (to close the Select Ta-
blespace window), and then on the Create one from the Create User page
(see figure 1.40 above).
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
43
A new user with the name you chose (in this example, LAB_DB) is
created and added to the Users set:
Figure 1.42 Oracle Enterprise Manager Users page (with the newly
created LAB_DB user selected)
Click on the Edit button: the Edit User page opens in its second Roles tab:
Figure 1.43 Oracle Enterprise Manager Edit User page (Roles tab, before
adding other roles than the default CONNECT one)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
44
Click on the Edit List button to open the Modify Roles page; add to the
CONNECT existing role CTXAPP, DBA, and RESOURCE ones too, and
then click OK:
Figure 1.44 Oracle Enterprise Manager Modify Roles page
Figure 1.45 Oracle Enterprise Manager Edit User page (Roles tab, after
adding other roles than the default CONNECT one)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
45
Click on the System Privileges tab:
Figure 1.46 Oracle Enterprise Manager Edit User page (System Privileges
tab, before adding other privileges than the default CREATE SESSION)
Click on the Edit List button to open the System Privileges page; add to
the CREATE SESSION existing privilege any other ones too (for a
possible, typical subset, please see the SQL script below, from GRANT
ADVISOR TO "LAB_DB" WITH ADMIN OPTION to GRANT UPDATE ANY
TABLE TO "LAB_DB"; especially, do not forget the UNLIMITED
TABLESPACE one), and then click OK:
Figure 1.47 Oracle Enterprise Manager System Privileges page
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
46
Note that selecting system privileges may take more than 15’, so split
them into groups, click OK after ending each group and the re-click Edit
List for selecting the following group; otherwise, Oracle will disconnect
you automatically after each 15’ (of “inactivity”) and you lose all of your
selections. When finishing selections, click on the Admin Options check
boxes (on all chosen system privileges, that might span on several pages!)
and then on the Apply button:
Figure 1.48 Oracle Enterprise Manager Edit User page (System Privileges
tab, after adding other privileges than the default CREATE SESSION)
Click on the Object Privileges tab, check all Grant Options check boxes
and then click on the Apply button:
Figure 1.49 Oracle Enterprise Manager Edit User (Object Privileges)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
47
Click on the Quotas tab, just to make sure that your db was granted the
Unlimited Tablespace System Privilege:
Figure 1.50 Oracle Enterprise Manager Edit User page (Quotas tab)
Note that same things could have been achieved by running the following
DDL script (either in the SQL window of the Enterprise Manager, or at
the SQL* command prompt):
CREATE USER "LAB_DB" PROFILE "DEFAULT"
IDENTIFIED BY "*******" DEFAULT TABLESPACE "USERS"
TEMPORARY TABLESPACE "TEMP" ACCOUNT UNLOCK
GRANT CREATE SESSION TO "LAB_DB"
GRANT EXECUTE
ON "APEX_030200"."WWV_FLOW_EPG_INCLUDE_MODULES"
TO "LAB_DB"
GRANT ALTER ON "FLOWS_FILES"."WWV_FLOW_FILE_OBJECTS$"
TO "LAB_DB"
GRANT DELETE ON "FLOWS_FILES"."WWV_FLOW_FILE_OBJECTS$"
TO "LAB_DB"
GRANT FLASHBACK ON "FLOWS_FILES"."WWV_FLOW_FILE_OBJECTS$"
TO "LAB_DB"
GRANT INDEX ON "FLOWS_FILES"."WWV_FLOW_FILE_OBJECTS$"
TO "LAB_DB"
GRANT INSERT ON "FLOWS_FILES"."WWV_FLOW_FILE_OBJECTS$"
TO "LAB_DB"
GRANT ON COMMIT REFRESH
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
48
ON "FLOWS_FILES"."WWV_FLOW_FILE_OBJECTS$"
TO "LAB_DB"
GRANT QUERY REWRITE
ON "FLOWS_FILES"."WWV_FLOW_FILE_OBJECTS$" TO "LAB_DB"
GRANT REFERENCES ON "FLOWS_FILES"."WWV_FLOW_FILE_OBJECTS$"
TO "LAB_DB"
GRANT SELECT ON "FLOWS_FILES"."WWV_FLOW_FILE_OBJECTS$"
TO "LAB_DB"
GRANT UPDATE ON "FLOWS_FILES"."WWV_FLOW_FILE_OBJECTS$"
TO "LAB_DB"
GRANT "CONNECT" TO "LAB_DB"
GRANT "CTXAPP" TO "LAB_DB"
GRANT "DBA" TO "LAB_DB"
GRANT "RESOURCE" TO "LAB_DB"
ALTER USER "LAB_DB" DEFAULT ROLE ALL
GRANT ADVISOR TO "LAB_DB" WITH ADMIN OPTION
GRANT ALTER ANY INDEX TO "LAB_DB" WITH ADMIN OPTION
GRANT ALTER ANY PROCEDURE TO "LAB_DB" WITH ADMIN OPTION
GRANT ALTER ANY SEQUENCE TO "LAB_DB" WITH ADMIN OPTION
GRANT ALTER ANY TABLE TO "LAB_DB" WITH ADMIN OPTION
GRANT ALTER ANY TRIGGER TO "LAB_DB" WITH ADMIN OPTION
GRANT ALTER DATABASE TO "LAB_DB" WITH ADMIN OPTION
GRANT ALTER SESSION TO "LAB_DB" WITH ADMIN OPTION
GRANT ALTER SYSTEM TO "LAB_DB" WITH ADMIN OPTION
GRANT ALTER TABLESPACE TO "LAB_DB" WITH ADMIN OPTION
GRANT ALTER USER TO "LAB_DB" WITH ADMIN OPTION
GRANT BACKUP ANY TABLE TO "LAB_DB" WITH ADMIN OPTION
GRANT COMMENT ANY TABLE TO "LAB_DB" WITH ADMIN OPTION
GRANT CREATE ANY INDEX TO "LAB_DB" WITH ADMIN OPTION
GRANT CREATE ANY PROCEDURE TO "LAB_DB" WITH ADMIN OPTION
GRANT CREATE ANY SEQUENCE TO "LAB_DB" WITH ADMIN OPTION
GRANT CREATE ANY TABLE TO "LAB_DB" WITH ADMIN OPTION
GRANT CREATE ANY TRIGGER TO "LAB_DB" WITH ADMIN OPTION
GRANT CREATE ANY VIEW TO "LAB_DB" WITH ADMIN OPTION
GRANT CREATE DATABASE LINK TO "LAB_DB" WITH ADMIN OPTION
GRANT CREATE SESSION TO "LAB_DB" WITH ADMIN OPTION
GRANT CREATE USER TO "LAB_DB" WITH ADMIN OPTION
GRANT DEBUG ANY PROCEDURE TO "LAB_DB" WITH ADMIN OPTION
GRANT DEBUG CONNECT SESSION TO "LAB_DB" WITH ADMIN OPTION
GRANT DELETE ANY TABLE TO "LAB_DB" WITH ADMIN OPTION
GRANT DROP ANY INDEX TO "LAB_DB" WITH ADMIN OPTION
GRANT DROP ANY PROCEDURE TO "LAB_DB" WITH ADMIN OPTION
GRANT DROP ANY SEQUENCE TO "LAB_DB" WITH ADMIN OPTION
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
49
GRANT DROP ANY TABLE TO "LAB_DB" WITH ADMIN OPTION
GRANT DROP ANY TRIGGER TO "LAB_DB" WITH ADMIN OPTION
GRANT DROP ANY VIEW TO "LAB_DB" WITH ADMIN OPTION
GRANT DROP TABLESPACE TO "LAB_DB" WITH ADMIN OPTION
GRANT DROP USER TO "LAB_DB" WITH ADMIN OPTION
GRANT EXECUTE ANY PROCEDURE TO "LAB_DB" WITH ADMIN OPTION
GRANT EXECUTE ANY PROGRAM TO "LAB_DB" WITH ADMIN OPTION
GRANT EXPORT FULL DATABASE TO "LAB_DB" WITH ADMIN OPTION
GRANT GRANT ANY OBJECT PRIVILEGE TO "LAB_DB"
WITH ADMIN OPTION
GRANT GRANT ANY PRIVILEGE TO "LAB_DB" WITH ADMIN OPTION
GRANT IMPORT FULL DATABASE TO "LAB_DB" WITH ADMIN OPTION
GRANT INSERT ANY TABLE TO "LAB_DB" WITH ADMIN OPTION
GRANT MANAGE TABLESPACE TO "LAB_DB" WITH ADMIN OPTION
GRANT ON COMMIT REFRESH TO "LAB_DB" WITH ADMIN OPTION
GRANT SELECT ANY SEQUENCE TO "LAB_DB"
GRANT SELECT ANY TABLE TO "LAB_DB"
GRANT SELECT ANY TRANSACTION TO "LAB_DB"
GRANT SYSDBA TO "LAB_DB"
GRANT UNLIMITED TABLESPACE TO "LAB_DB"
GRANT UPDATE ANY TABLE TO "LAB_DB"
From now on, you can connect to and work with your new LAB_DB db;
the easiest tool to use is the Oracle’s SQL Developer (freely downloada-
ble from Oracle’s website); install and open it:
Figure 1.51 Oracle’s SQL Developer About page
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
50
Click on the + (New Connection), fill in all necessary connection data,
click on Save, then on Test (if connection data is correct, “Status: Suc-
cess” is displayed in the left bottom corner) and then on Connect:
Figure 1.52 Oracle’s SQL Developer Db Connection page
A corresponding SQL sheet is opened and you can start working with
your LAB_DB db:
Figure 1.53 Oracle’s SQL Developer main page
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
51
1.2.5 Creating tables in Oracle with the SQL Developer GUI
Open the LAB_DB tree view, right-click on the Tables node, and click on
New Table:
Figure 1.54 Oracle’s SQL Developer tree view of a db objects
Figure 1.55 Oracle’s SQL Developer standard Create Table page
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
52
Click on the Advanced check box:
Figure 1.56 Oracle’s SQL Developer advanced Create Table page
Type COUNTRIES in the table Name text box (overwriting TABLE1),
click on the Comment node and type “Word countries of interest” in the
Comment text box:
Figure 1.57 Associating a comment to an Oracle table
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
53
Go back to the Columns node, type x in the first row of the column one
(overwriting COLUMN1), choose Number5 for Type, 3 for Precision
(there are only some 250 countries), 0 for Scale (cardinals are naturals),
check the Cannot be NULL box, and type “Primary surrogate COUN-
TRIES key” in the Comment text one:
Figure 1.58 Defining a surrogate key in Oracle
Click on the Primary Key, move X (note that Oracle only uses capital let-
ters) from the Available to the Selected Columns:
Figure 1.59 Defining the surrogate key to be the primary one in Oracle
5 Note that Oracle does not provide the autonumber data type.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
54
Go back to the Columns node, click on the + (add column) green button,
type Country in Name, leave the VARCHAR2 default Type, type 255 for
Size, “Country name” in Comment, and check the Cannot be NULL box6:
Figure 1.60 Adding the text field Country to COUNTRIES in Oracle
Click on the Unique Constraints node, then on Add, and move COUN-
TRY from Available to Selected Columns – in order to declare it as a key
(as there may not be two countries having same names):
Figure 1.61 Defining single keys constraints in Oracle
6 Note that Oracle is not providing automatic rejection of dirty nulls.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
55
Go back to the Columns node, click on the + (add column) green button,
type Nationality in Name, leave the VARCHAR2 default Type, type 32 for
Size, and “Main country's people nationality” in Comment:
Figure 1.62 Defining text fields that accept nulls in Oracle
Click once more on the + (add column) green button, type Population in
Name, choose Number for Type, 10 for Precision (which allows for a ma-
ximum value of 9,999,999,999), 0 for Scale, and type “Country popula-
tion” in the Comment text one:
Figure 1.63 Defining number fields that accept nulls in Oracle
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
56
Click on the Check Constraints node, then on Add, and type “POPULA-
TION BETWEEN 100 AND 2000000000” in the Condition text box (to
implement the corresponding (co) domain constraint)7:
Figure 1.64 Defining (co-)domain constraints in Oracle
As Capital cannot be fully implemented for the time being (because there
is no table CITIES yet), save and close this table scheme, by clicking on
OK. You can review it by clicking on its name:
Figure 1.65 Inspecting table columns with Oracle’s SQL Developer
7 Note that, unfortunately, Oracle does not provide either check constraints correspond-
ing error messages (see, for example, Access’ Validation Text) or number formatting
(see, for example, Access’ Standard Format).
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
57
To inspect table’s constraints, click on the Constraints tab; to also visua-
lize the columns to which a constraint refers, click on the desired con-
straint row:
Figure 1.66 Inspecting table constraints with Oracle’s SQL Developer
To inspect table’s indexes, click on the Indexes tab; to also visualize the
columns which are making up an index, click on the desired index row:
Figure 1.67 Inspecting table indexes with Oracle’s SQL Developer
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
58
To inspect table’s details, click on the Details tab:
Figure 1.68 Inspecting table details with Oracle’s SQL Developer
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
59
You can also inspect the corresponding SQL DDL statements, by clicking
on the SQL tab:
Figure 1.69 Inspecting corresponding table creation SQL DDL script with
Oracle’s SQL Developer
In order to automatically generate unique numbers for the primary surro-
gate key X, you first need to declare a corresponding sequence: right click
on the Sequences node of the LAB_DB scheme tree and then choose New
Sequence:
Figure 1.70 Creating a sequence with Oracle’s SQL Developer
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
60
Fill-in corresponding desired values for Name, Increment, Start with, Min,
and Max values, and then click OK:
Figure 1.71 Filling/editing sequence data with Oracle’s SQL Developer
Next, you need to create a trigger: right click on the Triggers node of the
LAB_DB scheme tree and then choose New Trigger:
Figure 1.72 Creating a trigger with Oracle’s SQL Developer
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
61
In the Create Trigger window that opens, fill-in Name, Table Name,
Insert, Before, Statement Level, and then click OK:
Figure 1.73 Specifying trigger details with Oracle’s SQL Developer
The corresponding trigger is created, but with a null; PL/SQL body:
Figure 1.74 Initial, null; body Oracle trigger PL/SQL code
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
62
Replace null; with:
if (:new.x is null) then
select countries_seq.nextval into :new.x from dual;
end if;
and save it (by clicking on the floppy-disk icon):
Figure 1.75 Final Oracle trigger PL/SQL code
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
63
You may check that this trigger is depending on table COUNTRIES and
sequence COUNTRIES_SEQ by clicking on the Dependencies tab:
Figure 1.76 Oracle trigger dependencies example
and inspect its details by clicking on the Details tab:
Figure 1.77 Oracle trigger details example
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
64
For entering data into table COUNTRIES, click its Data tab, then, for
each new line, on the green Insert Row icon (or type Ctrl+I or Tab at each
line end), and type corresponding data for Country, Nationality, and Po-
pulation into its instance:
Figure 1.78 Entering data in Oracle
Continue entering data at least on U.S.A., and U.K., by consulting the
Internet for their corresponding populations. Note that X’s values are
automatically filled-in (by the above defined trigger) and that you can
enter any ASCII characters (not only the first 128 standard ones), like, for
example, the Romanian’s ‘â’, ‘ș’, ‘ț’, etc.
Save entered data by clicking on the Refresh icon (or type Ctrl+R) and
then on Yes:
Figure 1.79 Saving data in Oracle
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
65
Similarly, create table CITIES with x as primary surrogate key and City,
and Country (with exactly same Type, Precision, and Scale like x in
COUNTRIES):
Figure 1.80 Creating table CITIES in Oracle
Click on the Foreign Keys node, then on Add, choose COUNTRIES as Re-
ferenced Table, COUNTRIES_PK as Referenced Constraint, and Country
as Local Column:
Figure 1.81 Implementing foreign keys in Oracle
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
66
Note that, unfortunately, Oracle does not provide an Access type Lookup
facility, so that you should build combo boxes (drop down lists) for all fo-
reign keys, such as to hide pointers and replace them with corresponding
semantic keys values in your db applications.
Also note that the default Restrict On Delete option will prevent users to
delete from COUNTRIES any row which is referred to by at least one row
from CITIES8. Dually, note that failing to enforce this referential integrity
(foreign key) constraint would potentially result in inconsistent (hence,
implausible) data instances: for example, they might delete the row from
Romania, leaving in CITIES “dangling pointers” (i.e. pointers pointing to
inexistent values) for all Romanian cities.
Click the Unique Constraints node, then the Add button, move City and
Country from the Available to the Selected Columns, in order to enforce
the City Country : CITIES ASCII(128) COUNTRIES minimally
one-to-oneness:
Figure 1.82 Defining a product (compound, concatenated) key in Oracle
8 Except for very rare, generally system cases (for users ones always get their prior
written approval for it!), DO NOT USE the Cascade Delete option: its effect is that
when users are deleting a row from COUNTRIES, all corresponding rows from CITIES
will be deleted too Imagine, for example, that Vodafone’s world integrated db would
have all foreign keys declared with the Cascade Delete option: trivially, when deleting
from CONTINENTS the row corresponding to Europe, all Europe’s countries, cities,
customers, invoices, calls, SMSs, etc. would be deleted too!
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
67
Click on OK to save the CITIES table scheme and then, similarly, create a
CITIES_SEQ sequence (with maximum value 999999) and a CI-
TIES_AUTONUM trigger (with select cities_seq.nextval into
:new.x from dual; in its body).
Open CITIES Data tab and enter at least data for Bucharest, Chișinău,
Washington, and London (note that you have to pay attention to actual
COUNTRIES.x values in order to correctly specify CITIES.Country ones):
Figure 1.83 Entering data in Oracle CITIES table instance
Re-open COUNTRIES in design mode (right-click on its name and choose
Edit) for similarly adding Capital to it; after saving the corresponding
foreign key, enter data in this column too:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
68
Figure 1.84 Entering data in Oracle COUNTRIES.Capital instance
1.2.6 Creating tables in Oracle with SQL DDL statements
The following Oracle DDL SQL statements are creating absolutely
similar tables as above:
--------------------------------------------------------
-- DDL for Sequence COUNTRIES_SEQ
--------------------------------------------------------
CREATE SEQUENCE "LAB_DB"."COUNTRIES_SEQ" MINVALUE 1
MAXVALUE 300 INCREMENT BY 1 START WITH 1 NOCACHE
ORDER NOCYCLE ;
--------------------------------------------------------
-- DDL for Sequence CITIES_SEQ
--------------------------------------------------------
CREATE SEQUENCE "LAB_DB"."CITIES_SEQ" MINVALUE 1
MAXVALUE 999999 INCREMENT BY 1 START WITH 1 NOCACHE
ORDER NOCYCLE ;
--------------------------------------------------------
-- DDL for Table COUNTRIES
--------------------------------------------------------
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
69
CREATE TABLE "LAB_DB"."COUNTRIES"
( "X" NUMBER(3,0),
"COUNTRY" VARCHAR2(255 BYTE),
"NATIONALITY" VARCHAR2(32 BYTE),
"POPULATION" NUMBER(10,0),
"CAPITAL" NUMBER(10,0)
) TABLESPACE "USERS" ;
COMMENT ON COLUMN "LAB_DB"."COUNTRIES"."X" IS 'Primary
surrogate COUNTRIES key';
COMMENT ON COLUMN "LAB_DB"."COUNTRIES"."COUNTRY" IS
'Country name';
COMMENT ON COLUMN "LAB_DB"."COUNTRIES"."NATIONALITY" IS
'Main country''s people nationality';
COMMENT ON COLUMN "LAB_DB"."COUNTRIES"."POPULATION" IS
'Country population';
COMMENT ON COLUMN "LAB_DB"."COUNTRIES"."CAPITAL" IS
'Country capital city;
COMMENT ON TABLE "LAB_DB"."COUNTRIES" IS 'World
countries of interest.';
--------------------------------------------------------
-- DDL for Index COUNTRIES_PK
--------------------------------------------------------
CREATE UNIQUE INDEX "LAB_DB"."COUNTRIES_PK" ON
"LAB_DB"."COUNTRIES" ("X") TABLESPACE "USERS" ;
--------------------------------------------------------
-- DDL for Index COUNTRIES_UK1
--------------------------------------------------------
CREATE UNIQUE INDEX "LAB_DB"."COUNTRIES_UK1" ON
"LAB_DB"."COUNTRIES" ("COUNTRY") TABLESPACE "USERS" ;
--------------------------------------------------------
-- DDL for Index COUNTRIES_UK2
--------------------------------------------------------
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
70
CREATE UNIQUE INDEX "LAB_DB"."COUNTRIES_UK2" ON
"LAB_DB"."COUNTRIES" ("CAPITAL") TABLESPACE "USERS" ;
--------------------------------------------------------
-- Constraints for Table COUNTRIES
--------------------------------------------------------
ALTER TABLE "LAB_DB"."COUNTRIES" ADD CONSTRAINT
"COUNTRIES_CHK1" CHECK (POPULATION BETWEEN 100 AND
2000000000) ENABLE;
ALTER TABLE "LAB_DB"."COUNTRIES" ADD CONSTRAINT
"COUNTRIES_PK" PRIMARY KEY ("X")
USING INDEX TABLESPACE "USERS" ENABLE;
ALTER TABLE "LAB_DB"."COUNTRIES" ADD CONSTRAINT
"COUNTRIES_UK1" UNIQUE ("COUNTRY")
USING INDEX TABLESPACE "USERS" ENABLE;
ALTER TABLE "LAB_DB"."COUNTRIES" ADD CONSTRAINT
"COUNTRIES_UK2" UNIQUE ("CAPITAL")
USING INDEX TABLESPACE "USERS" ENABLE;
ALTER TABLE "LAB_DB"."COUNTRIES" MODIFY ("X" NOT NULL
ENABLE);
ALTER TABLE "LAB_DB"."COUNTRIES" MODIFY ("COUNTRY" NOT
NULL ENABLE);
--------------------------------------------------------
-- DDL for Trigger COUNTRIES_AUTONUM
--------------------------------------------------------
CREATE OR REPLACE TRIGGER "LAB_DB"."COUNTRIES_AUTONUM"
before insert on countries
for each row
begin
if (:new.x is null) then
select countries_seq.nextval into :new.x from dual;
end if;
end;
/
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
71
ALTER TRIGGER "LAB_DB"."COUNTRIES_AUTONUM" ENABLE;
--------------------------------------------------------
-- DDL for Table CITIES
--------------------------------------------------------
CREATE TABLE "LAB_DB"."CITIES"
( "X" NUMBER(6,0),
"CITY" VARCHAR2(128 BYTE),
"COUNTRY" NUMBER(3,0)
) TABLESPACE "USERS" ;
COMMENT ON COLUMN "LAB_DB"."CITIES"."X" IS 'CITIES
surrogate primary key';
COMMENT ON COLUMN "LAB_DB"."CITIES"."CITY" IS 'City
name';
COMMENT ON COLUMN "LAB_DB"."CITIES"."COUNTRY" IS
'Country to which city belongs to';
COMMENT ON TABLE "LAB_DB"."CITIES" IS 'World cities of
interest.';
--------------------------------------------------------
-- DDL for Index CITIES_PK
--------------------------------------------------------
CREATE UNIQUE INDEX "LAB_DB"."CITIES_PK" ON
"LAB_DB"."CITIES" ("X") TABLESPACE "USERS" ;
--------------------------------------------------------
-- DDL for Index CITIES_UK1
--------------------------------------------------------
CREATE UNIQUE INDEX "LAB_DB"."CITIES_UK1" ON
"LAB_DB"."CITIES" ("CITY", "COUNTRY") TABLESPACE "USERS" ;
--------------------------------------------------------
-- Constraints for Table CITIES
--------------------------------------------------------
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
72
ALTER TABLE "LAB_DB"."CITIES" ADD CONSTRAINT "CITIES_PK"
PRIMARY KEY ("X") USING INDEX TABLESPACE "USERS" ENABLE;
ALTER TABLE "LAB_DB"."CITIES" ADD CONSTRAINT "CITIES_UK1"
UNIQUE ("CITY", "COUNTRY")
USING INDEX TABLESPACE "USERS" ENABLE;
ALTER TABLE "LAB_DB"."CITIES" MODIFY ("X" NOT NULL
ENABLE);
ALTER TABLE "LAB_DB"."CITIES" MODIFY ("CITY" NOT NULL
ENABLE);
ALTER TABLE "LAB_DB"."CITIES" MODIFY ("COUNTRY" NOT NULL
ENABLE);
--------------------------------------------------------
-- Ref Constraints for Table CITIES
--------------------------------------------------------
ALTER TABLE "LAB_DB"."CITIES" ADD CONSTRAINT
"CITIES_COUNTRIES_FK1" FOREIGN KEY ("COUNTRY")
REFERENCES "LAB_DB"."COUNTRIES" ("X") ENABLE;
--------------------------------------------------------
-- DDL for Trigger CITIES_AUTONUM
--------------------------------------------------------
CREATE OR REPLACE TRIGGER "LAB_DB"."CITIES_AUTONUM"
before insert on cities
for each row
begin
if (:new.x is null) then
select cities_seq.nextval into :new.x from dual;
end if;
end;
/
ALTER TRIGGER "LAB_DB"."CITIES_AUTONUM" ENABLE;
--------------------------------------------------------
-- Ref Constraints for Table COUNTRIES
--------------------------------------------------------
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
73
ALTER TABLE "LAB_DB"."COUNTRIES" ADD CONSTRAINT
"COUNTRIES_CITIES_FK1" FOREIGN KEY ("CAPITAL")
REFERENCES "LAB_DB"."CITIES" ("X") ENABLE;
In order to create and run such DDL queries in Oracle, you have to enter
them (or copy them from any text editor tool) in a SQL sheet of the SQL
Developer, select the corresponding db (LAB_DB, in this case) in its as-
sociated current db combo box, and then execute it by clicking the Run
Script icon (or pressing F5):
Figure 1.85 Executing SQL scripts with Oracle SQL Developer
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
74
1.3 Best practice rules
BPR1.0 For each object set create a corresponding table (giving it the
name of the set and an appropriate semantic description/comment),
having a surrogate numeric primary key x (or ID), preferably of
AutoNumber data type (if the set is not a subset); if this data type is
not available, then program it yourself (e.g., in Oracle, with sequences
and triggers); if the RDBMS allows it, correctly size its co-domain (that
should correspond to the maximum possible cardinal of that object set) .
BPR1.1 For each valuation function, add to the table corresponding
to its domain a column (having the name of the function and an ap-
propriate semantic description/comment) of the type corresponding
to its co-domain, as well as all relational type constraints that apply to
it (domain, NOT NULL, uniqueness, check /tuple).
BPR1.2 For each structural function, add to the table corresponding
to its domain a column (having the name of the function and an ap-
propriate semantic description/comment) of numeric type, of size cor-
responding to the maximum cardinality of its co-domain, declare it as
a foreign key referencing the primary key of the table corresponding
to its co-domain, and add all other relational type constraints that ap-
ply to it (domain, NOT NULL, uniqueness, check /tuple).
BPR1.3 If the RDBMS offers lookup combo boxes, use them for any
foreign key, in order to hide pointers and replace them for users with
corresponding semantic key values.
BPR1.4 Each table must have at least one column which does not ac-
cept nulls.
BPR1.5 Whenever RDBMS provides it, ban dirty nulls for all text
types columns (otherwise, you should program it in your db applica-
tions).
BPR1.6 Each table must have at least one semantic (candidate) key,
with only two exceptions: tables corresponding to subsets or to object
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
75
sets of type rabbit/poultry/etc. cages (note that even they may have
such keys, however!).
BPR1.7 If RDBMS provides a GUI, use it first, for increasing your
productivity, and use SQL only then, for refining db schemes, if ne-
eded, or when you need to generate/execute SQL statements pro-
grammatically.
1.4 Homework H1.1 Create a STATES table with (x, State name, Country, State code,
State telephone prefix, State capital, State population) and insert into it at
least 2 states per country.
H1.2 Add (both in Design mode and SQL) the following functions too (as
well as corresponding instance data):
- StateDesignation : COUNTRIES ASCII(255) NULLS, to
store the country’s administrative subdivision names (e.g in the
U.S. they are called ‘state’, in Romania ‘judet’, in Germany and
Austria ‘land’, etc.);
- CountryCode : COUNTRIES ASCII(8), to store the country’s
(vechicle plates) codes (e.g. USA, UK, RO, F, D, RU, etc.);
- Population : CITIES [10, 30000000] NULLS
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
76
Chapter 2. 2nd Lab: Transactional DML statements in
SQL and GUI
2.1 Transactional SQL DML statements There are 5 DML standard SQL statements:
INSERT INTO VALUES ... – inserts one row
SELECT ... – inserts n rows, n natural
UPDATE … SET.... [WHERE]– updates columns values
(for all rows, if no WHERE clause, or for only those rows
that satisfy the WHERE clause).
DELETE FROM ... [WHERE] – empties table (if no
WHERE clause) or removes from its instance only those
rows that satisfy the WHERE clause.
SELECT… INTO ... (Selects the needed data, creates the
table and stores in it the selected rows). Please, note that in
the tables created using SELECT INTO statements no
constraints are defined; so, if needed, you should add them
explicitly later.
SELECT... FROM... [WHERE]... [GROUP BY]....
[HAVING]... [ORDER BY] …
Execution of the first 4 statements is transactional or ACID (for SELECT
it is no need, as it only retrieves data), where:
A – Atomic: all involved rows (if no constraint is violated) or none (if at
least one value would violate at least one constraint) are affected by the
respective change;
C – Consistent: Although, sometimes, during a transaction, db con-
straints may be temporarily violated, at the end of any transaction the db
should remain in a consistent state (that is its instance may not violate any
of the db constraints).
I – Independent (Isolation): In concurrent environments, any transac-
tion should behave the same, just as if it were run alone, regardless of any
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
77
other concurrent transaction(that is simultaneously running concurrent
transactions are independent one of each other).
D – Durable: At the end of each successful transaction, all new data is
stored for ever as such: only subsequent transactions might modify them.
You can define an explicit transaction (containing any number of DML
and/or DDL statements) as:
BEGIN TRANS[ACTIONS] (transaction opening parenthesis)
...
COMMIT [TRANSACTION] – meaning that the modifications done are
ok and the user agrees to save the modified data. (OK transaction closing
parenthesis)
Or
ROLLBACK [TRANSACTION] – it’s the dual of COMMIT, meaning
that the user wants that the database be restored to the initial database in-
stance prior to modifications, as something went wrong (generally a con-
straint would be violated, but also lack of enough disk/memory free
space, etc.) (KO transaction closing parenthesis).
2.2 Exercises
2.2.1 Prerequisites in Access
a. The table STATES having the following structure should also be alrea-
dy created (see homework from 1st lab):
x- autonumber, Primary Key
State – Text(255), Required: Yes, Allow zero length: No (not allowing
dirty nulls)
Country – Required: Yes, lookup in COUNTRIES:
SELECT x, Country FROM COUNTRIES ORDER BY Country;
StateCode – Text (8), Allow zero length: NO
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
78
StateTelPrefix – integer, Validation rule: between 0 and 500
StateCapital - Required: No, lookup in CITIES:
SELECT CITIES.x, [City] & ", " & [STATES].[State]
& ", " & [COUNTRIES].[Country]
AS [City, State, Country]
FROM COUNTRIES INNER JOIN (STATES INNER JOIN CITIES
ON STATES.x = CITIES.State)
ON COUNTRIES.x = STATES.Country
ORDER BY [City] & ", " & [STATES].[State] & ", " &
[COUNTRIES].[Country];
Figure 2.1: STATES table scheme
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
79
Figure 2.2: STATES instance sample
Keys: StateCapital, State ● Country (we will later see that StateCode ●
Country and StateTelPrefix ● Country are keys also)
Figure 2.3: STATES keys
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
80
b. The table CITIES having the following new structure (see lab 1 and homework
from 1st lab):
Figure 2.4: CITIES instance sample
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
81
Figure 2.5: modified CITIES table scheme
Note that, by refining this model (also considering states), cities belong
now to countries transitively (not directly anymore), just like in reality,
through states: for example, Craiova belongs to Romania, as it belongs to
the Dolj state, which belongs to Romania. This is why former Country co-
lumn of CITIES has been replaced by a State one, whose values are
looked up for in STATES:
SELECT STATES.x, [State] & ", " &
[COUNTRIES].[COUNTRY] AS [State, Country]
FROM COUNTRIES INNER JOIN STATES
ON COUNTRIES.x = STATES.Country
ORDER BY [State] & ", " & [COUNTRIES].[COUNTRY];
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
82
Mathematically, former Country : CITIES COUNTRIES now becomes
a computable function (so not worth storing) * Country : CITIES
COUNTRIES, * Country = Country State, where Country : STATES
COUNTRIES and State : CITIES STATES.
Corresponding table relationships are shown in figure 2.8 below.
Obviously, CITIES’ key changed also (from City●Country to City●State):
Figure 2.6: new CITIES keys
Table COUNTRIES remains the same, just like in the 1st lab, except for
the Capital combo box, which now contains states too (see the lookup of
StateCapital above):
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
83
Figure 2.7: new CITIES instance sample
Figure 2.8: Relationships between the three db tables
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
84
2.2.2 Exercises in Access
P2.1. Insert into the db, both by using SQL and MS design mode (an im-
plementation of QBE = Query-by-Example), a city called Memphis as the
capital of the Tennessee state of the U.S.A. (considering that the U.S.A. id
is known: here, it is 3).
Solution:
Obviously, as no such rows exist and as State is compulsory in CITIES,
first we have to insert a corresponding row in STATES, then one in CI-
TIES, and finally declare Memphis as the corresponding state capital:
1.1 Insert Tennessee in STATES
For opening the design mode, click on Create, then Query Design, and
then Close on the Show Table modal popup window:
Figure 2.9: the Query Design Show Table modal popup window
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
85
Figure 2.10 shows the corresponding empty query; click on Append (to
change current query type from the default SELECT to the INSERT
type), select table STATES in its Table Name dropdown list and then click
OK (see figure 2.11).
Type “Tennessee” in the Field’s first cell and then 3 in the second one;
choose State in the AppendTo first cell (bellow “Tennessee”) and Country
in its second one (bellow 3) – see figure 2.12. Click View, then SQL View:
figure 2.13 shows the equivalent SQL query generated by the system.
Figure 2.10: the Query Design window for an empty query
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
86
Figure 2.11: the Query Design Append window for INSERT type queries
Figure 2.12: the Query Design window for the InsertTennessee query
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
87
Figure 2.13: the SQL window for the initial InsertTennessee query
Unfortunately, starting with Access 2010, the system is not generating
anymore INSERT … VALUES statements, but only SELECT ones;
moreover, as we did not select any table in the Show Table window, this
generated SQL statement is even syntactically wrong, as it lacks the
FROM clause; consequently, you have to correct it as follows (see figure
2.14):
INSERT INTO STATES (State, Country)
VALUES (“Tennessee”, 3);
Figure 2.14: the SQL window for the corrected InsertTennessee query
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
88
Note that you could have opened the SQL View mode immediately after
closing the Show Table window (figure 2.9 above) and type this whole
INSERT statement.
Click on Run: new corresponding row was added, with x = 8 (see figure
2.4 above); save it then as query insertTennessee: click on the Save icon
(the floppy disk one), type the desired query name and then click on OK
(see figure 2.15).
Figure 2.15: the Query Design Save As modal popup window
1.2 Insert Memphis in CITIES
Similarly as above, but choosing table STATES in the Show Table win-
dow (figure 2.9 above) and table CITIES in the AppendTo one (figure
2.11 above), as well as establishing criteria “Tennessee” for State and 3
for Country (see figure 2.16 below), the system generates:
INSERT INTO CITIES (City, State)
SELECT “Memphis”, x FROM STATES
WHERE State = “Tennessee” AND Country = 3;
When you run it, a new corresponding row was added, with x = 7 (see fi-
gure 2.2 above); save it as query insertMemphisTennessee.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
89
Figure 2.16: the Query Design window for the
insertMemphisTennessee query
1.3 Declare (in STATES) Memphis to be Tennessee‘s state capital
First, please note that, unfortunately, the following standard SQL syntax is
not accepted by Access:
UPDATE STATES SET StateCapital = (SELECT x FROM CITIES
WHERE City=”Memphis” AND State = (SELECT x FROM
STATES
WHERE Country= 3 AND State= “Tennessee”))
WHERE State = “Tennessee” AND Country = 3;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
90
Fortunately, Access provides a library function that is returning the first9 e
value from any SQL statement of the type SELECT TOP 1 e FROM T
WHERE F; its syntax is: DLookup(“e”, “T”, “F”).
For example, DLookup("x", "STATES", "State = 'Tennessee'
AND Country = 3") is computing x’s value for Tennessee from
STATES (7 - see figure 2.2 above); by nesting two calls to it,
DLookup("x", "CITIES", "City='Memphis' AND State = " &
DLookup("x", "STATES", "State = 'Tennessee' AND
Country = 3")) is returning x’s value for Memphis from CITIES (8 -
see figure 2.4 above)10
.
Consequently, the needed query for storing the fact that Memphis is the
capital of Tennessee is:
UPDATE STATES SET StateCapital = DLookup("x",
"CITIES", "City='Memphis' AND State = " &
DLookup("x", "STATES", "State = 'Tennessee' AND
Country = 3"))
WHERE State="Tennessee" AND Country=3;
To build it in MS QBE, open a new query in design mode, just like in 1.1
above, select table STATES in the Show Table modal popup window (see
figure 2.9), click on OK to close it, then on Update (to change current
query type from the default SELECT to the UPDATE type), double-click
the StateCapital column, type the above DLookup expression below it, in
the UpdateTo row’s first cell, double-click State, type “Tennessee” in the
corresponding Criteria cell below it, and double-click Country and type 3
in the corresponding Criteria cell below it (see figure 2.17).
9 Note that, here, first does not imply any ordering! If e is not void, Access is returning
an arbitrary “first” value from e (and when it is void, it returns NULL). Consequently,
DLookup should be used only when F includes a key, so e has only one element, or for
detecting whether or not a subset e is empty. 10
Note that & is the string concatenation operator in Access (VB/VBA/SQL, generally).
Also note that strings inside strings are enclosed in either double quotes or apostrophes.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
91
Figure 2.18 shows the corresponding generated SQL UPDATE statement
(that, again, we could have typed in directly, from the beginning, but
using QBE spares us many risks of syntactical errors).
Figure 2.17: the Query Design window for the
updateTennesseeCapital query
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
92
Figure 2.18: the SQL View window for the
updateTennesseeCapital query
Note that, trivially, this a programmatic approach, needed when dynami-
cally having to update data through db software applications (which is the
standard way to manage data, thus hiding both dbs and needed operatio-
nal workflows from end-users); same things may be accomplished stati-
cally, by non-programmer users, using the Access GUI: figures 2.19, 2.20,
and 2.21 are showing how to add state and its state capital Sibiu from
Romania.
Also note that all combo boxes (dropdown lists) of any table/form are
(automatically) computed only when the corresponding table/form is
opened; this is why, for example, in order to be able to select the Sibiu
state too in the CITIES.State combo box immediately after it has been in-
serted in STATES, you have to close and re-open CITIES11
.
11
Note, however, that this can also be done programmatically, in forms, without needing
to close and re-open them, with the VBA Requery method.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
93
Figure 2.19: Inserting a new state (Sibiu) using Access’ GUI
Figure 2.20: Inserting a new city (Sibiu) using Access’ GUI
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
94
Figure 2.21: Storing the fact that Sibiu is the capital of the homonym
state, by using Access’ GUI
P2.2. Parameterize the first insert statement of P1 above such as to insert
a new state in any desired existing country.
Solution:
Note that, unlike other RDBMSs, where parameters are syntactically re-
cognized by prefixing their names with some special character (e.g. @ for
MS SQL Server, : for Oracle, etc.), Access uses a semantic approach in-
stead: anywhere in a SQL statement where either a table or a column
name is not existing in the corresponding database, Access is considering
that name as being a parameter; Consequently, when running such a state-
ment Access will ask user for the actual value of all such parameters, by
prompting for each of them the name of the corresponding parameter.
Note that as prompts need white spaces and/or special characters, general-
ly, Access parameters are enclosed in square brackets.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
95
This is why the solution for parameterizing the first insert statement from
P2.1 above (see 1.1 of P2.1 above) is the following (save it as insert-
State):
INSERT INTO STATES (State, Country)
SELECT [Enter desired state name:], x
FROM COUNTRIES
WHERE Country = [Enter desired country
name:];
2.2.3 Prerequisites in Oracle
a. The table STATES having the following structure should also be alrea-
dy created (see homework from 1st lab):
x- autonumber, Primary Key
State – Text(255), NOT NULL: Yes
Country – NOT NULL: Yes, references COUNTRIES
StateCode – Text(8)
StateTelPrefix – integer, Validation rule: between 0 and 500
StateCapital - NOT NULL: No, references CITIES, unique (no city may
simultaneously be the capital of more than one state)
Keys: StateCapital, State ● Country (we will later see that StateCode ●
Country and StateTelPrefix ● Country are keys also)
Here is the corresponding SQL DDL script:
------------------------------------------------------
-- DDL for Sequence STATES_SEQ
------------------------------------------------------
CREATE SEQUENCE "LAB_DB"."STATES_SEQ" MINVALUE 1
MAXVALUE 9999 INCREMENT BY 1 START WITH 1 NOCACHE
ORDER NOCYCLE ;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
96
------------------------------------------------------
-- DDL for Table STATES
------------------------------------------------------
CREATE TABLE "LAB_DB"."STATES"
( "X" NUMBER(6,0),
"STATE" VARCHAR2(255 BYTE),
"COUNTRY" NUMBER(3,0),
"STATE_CODE" VARCHAR2(8 BYTE),
"STATE_TEL_PREFIX" NUMBER(3,0),
"CAPITAL" NUMBER(10,0)
) TABLESPACE "USERS" ;
COMMENT ON COLUMN "LAB_DB"."STATES"."X" IS 'STATES
surrogate primary key';
COMMENT ON COLUMN "LAB_DB"."STATES"."STATE" IS
'State name';
COMMENT ON COLUMN "LAB_DB"."STATES"."COUNTRY" IS
'Country to which state belongs';
COMMENT ON COLUMN "LAB_DB"."STATES"."STATE_CODE" IS
'State (vehicle plates) code';
COMMENT ON COLUMN
"LAB_DB"."STATES"."STATE_TEL_PREFIX" IS 'State
telephone prefix';
COMMENT ON COLUMN "LAB_DB"."STATES"."CAPITAL" IS
'State capital city';
COMMENT ON TABLE "LAB_DB"."STATES" IS 'The set of
countries sub-divisions of interest.';
------------------------------------------------------
-- DDL for Index STATES_PK
------------------------------------------------------
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
97
CREATE UNIQUE INDEX "LAB_DB"."STATES_PK" ON
"LAB_DB"."STATES" ("X") TABLESPACE "USERS" ;
------------------------------------------------------
-- DDL for Index STATES_UK1
------------------------------------------------------
CREATE UNIQUE INDEX "LAB_DB"."STATES_UK1" ON
"LAB_DB"."STATES" ("CAPITAL") TABLESPACE "USERS" ;
------------------------------------------------------
-- Constraints for Table STATES
------------------------------------------------------
ALTER TABLE "LAB_DB"."STATES" ADD CONSTRAINT
"STATES_CHK1" CHECK (STATE_TEL_PREFIX BETWEEN 0 AND
500) ENABLE;
ALTER TABLE "LAB_DB"."STATES" ADD CONSTRAINT
"STATES_PK" PRIMARY KEY ("X")
USING INDEX TABLESPACE "USERS" ENABLE;
ALTER TABLE "LAB_DB"."STATES" ADD CONSTRAINT
"STATES_UK1" UNIQUE ("CAPITAL")
USING INDEX TABLESPACE "USERS" ENABLE;
ALTER TABLE "LAB_DB"."STATES" ADD CONSTRAINT
"STATES_UK2" UNIQUE ("STATE", "COUNTRY")
USING INDEX TABLESPACE "USERS" ENABLE;
ALTER TABLE "LAB_DB"."STATES" MODIFY ("X" NOT NULL
ENABLE);
ALTER TABLE "LAB_DB"."STATES" MODIFY ("STATE" NOT
NULL ENABLE);
ALTER TABLE "LAB_DB"."STATES" MODIFY ("COUNTRY" NOT
NULL ENABLE);
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
98
------------------------------------------------------
-- Ref Constraints for Table STATES
------------------------------------------------------
ALTER TABLE "LAB_DB"."STATES" ADD CONSTRAINT
"STATES_CITIES_FK1" FOREIGN KEY ("CAPITAL")
REFERENCES "LAB_DB"."CITIES" ("X") ENABLE;
ALTER TABLE "LAB_DB"."STATES" ADD CONSTRAINT
"STATES_COUNTRIES_FK1" FOREIGN KEY ("COUNTRY")
REFERENCES "LAB_DB"."COUNTRIES" ("X") ENABLE;
------------------------------------------------------
-- DDL for Trigger STATES_AUTONUM
------------------------------------------------------
CREATE OR REPLACE TRIGGER "LAB_DB"."STATES_AUTONUM"
before insert on states
for each row
begin
if (:new.x is null) then
select states_seq.nextval into :new.x from dual;
end if;
end;
/
ALTER TRIGGER "LAB_DB"."STATES_AUTONUM" ENABLE;
Then, please also add some lines to its instance:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
99
Figure 2.22: STATES instance sample
b. The table CITIES having the following new structure (see lab 1 and homework
from 1st lab):
Note that, by refining this model (also considering states), cities belong
now to countries transitively (not directly anymore), just like in reality,
through states: for example, Craiova belongs to Romania, as it belongs to
the Dolj state, which belongs to Romania. This is why former Country co-
lumn of CITIES has been replaced by a State one, whose values are
referencing those in STATES.x.
Mathematically, former Country : CITIES COUNTRIES now becomes
a computable function (so not worth storing) * Country : CITIES
COUNTRIES, * Country = Country State, where Country : STATES
COUNTRIES and State : CITIES STATES.
Obviously, CITIES’ key changed also (from City●Country to City●State).
In order to achieve all these, you should first drop the Country former
foreign key, by executing the following DDL statement:
ALTER TABLE CITIES DROP CONSTRAINT CITIES_COUNTRIES_FK1;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
100
Then, you can rename Country to State (the easiest for doing it is to use
SQL Developer’s GUI): note that this is also automatically changing the
key City●Country to City●State.
Then, you have to correctly update data from the State column:
Figure 2.23: CITIES instance sample
Now, you can add the referential integrity constraint for State.
The corresponding new DDL SQL script for this table is the following
(note that the one for its sequence and trigger is not displayed anymore, as
they did not change):
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
101
------------------------------------------------------
-- DDL for Table CITIES
------------------------------------------------------
CREATE TABLE "LAB_DB"."CITIES"
( "X" NUMBER(6,0),
"CITY" VARCHAR2(128 BYTE),
"STATE" NUMBER(6,0)
) TABLESPACE "USERS" ;
COMMENT ON COLUMN "LAB_DB"."CITIES"."X" IS 'CITIES
surrogate primary key';
COMMENT ON COLUMN "LAB_DB"."CITIES"."CITY" IS 'City
name';
COMMENT ON COLUMN "LAB_DB"."CITIES"."STATE" IS
'Country to which city belongs to';
COMMENT ON TABLE "LAB_DB"."CITIES" IS 'World
cities of interest.';
------------------------------------------------------
-- DDL for Index CITIES_PK
------------------------------------------------------
CREATE UNIQUE INDEX "LAB_DB"."CITIES_PK" ON
"LAB_DB"."CITIES" ("X") TABLESPACE "USERS" ;
------------------------------------------------------
-- DDL for Index CITIES_UK1
------------------------------------------------------
CREATE UNIQUE INDEX "LAB_DB"."CITIES_UK1" ON
"LAB_DB"."CITIES" ("CITY", "STATE") TABLESPACE "USERS"
;
------------------------------------------------------
-- Constraints for Table CITIES
------------------------------------------------------
ALTER TABLE "LAB_DB"."CITIES" ADD CONSTRAINT
"CITIES_PK" PRIMARY KEY ("X")
USING INDEX TABLESPACE "USERS" ENABLE;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
102
ALTER TABLE "LAB_DB"."CITIES" ADD CONSTRAINT
"CITIES_UK1" UNIQUE ("CITY", "STATE")
USING INDEX TABLESPACE "USERS" ENABLE;
ALTER TABLE "LAB_DB"."CITIES" MODIFY ("X" NOT NULL
ENABLE);
ALTER TABLE "LAB_DB"."CITIES" MODIFY ("CITY" NOT
NULL ENABLE);
ALTER TABLE "LAB_DB"."CITIES" MODIFY ("STATE" NOT
NULL ENABLE);
------------------------------------------------------
-- Ref Constraints for Table CITIES
------------------------------------------------------
ALTER TABLE "LAB_DB"."CITIES" ADD CONSTRAINT
"CITIES_STATES_FK1" FOREIGN KEY ("STATE")
REFERENCES "LAB_DB"."STATES" ("X") ENABLE;
Note that table COUNTRIES remains the same, just like in the 1st lab.
2.2.4 Exercises in Oracle
P2.1. Insert into the db, by using SQL12
, a city called Memphis as the ca-
pital of the Tennessee state of the U.S.A. (considering that the U.S.A. id is
known: here, it is 7).
Solution:
Obviously, as no such rows exist and as State is compulsory in CITIES,
first we have to insert a corresponding row in STATES, then one in CI-
TIES, and finally declare Memphis as the corresponding state capital:
12 Note that Oracle’s SQL Developer does not provide QBE (= Query-by-
Example); QBE is provided only by the Oracle’s ADF (= Application De-
velopment Framework, including its free, limited Essentials version),
which is requiring a Java IDE and an Application Server too.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
103
1.1 Insert Tennessee in STATES
Open a SQL tab and attach to it the LAB_DB db (see figure 1.83 above);
type in it the following DML statement:
INSERT INTO STATES (State, Country)
VALUES (‘Tennessee’, 7);
Click on the Run Statement icon (the green triangle pointing to the right)
or press Ctrl+Enter to execute it: the Script Output pane will display “1
rows inserted”, whereas the Messages – Log one will contain a “Commit
Successful” message.
Figure 2.24 Programmatically insertion of a line in a table
The new line was indeed inserted (with x = 11):
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
104
Figure 2.25 STATES table’s instance after inserting Tennessee
You can save the pane (by clicking on the floppy-disk icon) as a .sql text
file, but you can also save only this statement as a procedure: right-click
on the Procedures node and click on New Procedure:
Figure 2.26 Creating a new PL/SQL procedure in Oracle SQL Developer
In the Create PL/SQL Procedure window that opens, type “insert_
Tennessee” in the Name text box and then click on OK:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
105
Figure 2.27 Naming a new PL/SQL procedure in Oracle SQL Developer
Note that the body of any newly created SQL/PL procedure is null:
Figure 2.28 The initial null body of a new Oracle PL/SQL procedure
Replace the null with the above INSERT statement and save the proce-
dure:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
106
Figure 2.29 The final INSERT_TENNESSEE Oracle PL/SQL procedure
In order to run it, right-click on its name and then click on the Run option;
click then OK in the Run PL/SQL window that opens:
Figure 2.30 Running an Oracle PL/SQL procedure
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
107
As you should expect it, no other line is inserted in STATES, as this would
violate the key State ● Country; any such attempt is not only rejected, but
a corresponding error (red) message is displayed (ORA-00001: unique
constraint (LAB_DB.STATES_UK2) violated ORA-06512: at "LAB_DB
.INSERT_TENNESSEE", line 3 ORA-06512: at line 2):
Figure 2.31 Example of an Oracle PL/SQL procedure error message
1.2 Insert Memphis in CITIES
Similarly as above, you should type in the SQL pane of LAB_DB and
then execute the following statement:
INSERT INTO CITIES (City, State)
SELECT ‘Memphis’, x FROM STATES
WHERE State = ‘Tennessee’ AND Country = 7;
A new corresponding row was added, with X = 44 (see figure 2.32 be-
low); save it as query insertMemphisTennessee.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
108
Figure 2.32: Table CITIES’ instance after inserting Memphis,
Tennessee
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
109
1.3 Declare (in STATES) Memphis to be Tennessee‘s state capital
Fortunately, the following standard SQL syntax is accepted by Oracle:
UPDATE STATES SET Capital =
(SELECT x FROM CITIES
WHERE City=’Memphis’ AND State =
(SELECT x FROM STATES
WHERE Country= 7 AND State=
‘Tennessee’))
WHERE State = ‘Tennessee’ AND Country = 7;
You may either enter it in the SQL pane of LAB_DB and then run it or
save it as the updateTennesseeCapital PL/SQL procedure and execute it;
the result is that the capital city for Tennessee is from now on 44 instead
of null (compare with figure 2.25 above):
Figure 2.33: STATES table’s instance after updating Tennessee’s capital
Note that, trivially, this a programmatic approach, needed when dynami-
cally having to update data through db software applications (which is the
standard way to manage data, thus hiding both dbs and needed operatio-
nal workflows from end-users); obviously, same things may be accom-
plished statically, by non-programmer users, using the Oracle SQL Deve-
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
110
loper GUI: figures 2.34, 2.35, and 2.36 are showing how to add state and
its state capital Sibiu from Romania.
Figure 2.34: Inserting a new state (Sibiu) using Oracle SQL Developer
Figure 2.35: Inserting a new city (Sibiu) using Oracle SQL Developer
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
111
Figure 2.36: Storing the fact that Sibiu is the capital of the homonym
state, by using Oracle SQL Developer
P2.2. Parameterize the first insert statement of P2.1 above such as to in-
sert a new state in any desired existing country.
Solution:
Note that, Oracle requires for its native dynamic SQL parameters valid
variable names prefixed by ‘:’. Consequently, when running such a state-
ment, Oracle will ask users for the actual value of all such parameters.
This is why the solution for parameterizing the first insert statement from
P2.1 above (see 1.1 of P2.1 above) is the following:
INSERT INTO STATES (State, Country)
SELECT :EnterDesiredStateName, x
FROM COUNTRIES
WHERE Country = :EnterDesiredCountrName;
Figures 2.37 and 2.38 below show how actual parameter values are
passed through when running this query from a SQL pane of DB_LAB
(you can select any parameter by clicking on its name; you enter corres-
ponding actual values into the Value text box; when ready, click Apply);
figure 2.39 presents the new STATES table instance, including state Dolj.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
112
Figure 2.37: Providing actual EnterDesiredStateName parameter value
Figure 2.38: Providing actual EnterDesiredCountryName parameter value
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
113
Figure 2.39: STATES instance after inserting state Dolj
When such parameterized queries need to be run either frequently and/or
programmatically, the best solution is to store them as a PL/SQL proce-
dure; for example, create and save (similar to figures 2.26 to 2.29 above)
procedure Insert_State. Figure 2.40 shows how to give names, besides to
the procedure itself, to its parameters too. Figure 2.41 shows the corres-
ponding initial code, with a null body. Figure 2.42 shows the final code:
note that in PL/SQL you should not use the prefix ‘:’ for parameters, as
they are explicitly defined, just like in any other high level programming
language.
Figure 2.43 presents how to enter actual parameter values when running
the procedure from the Oracle SQL Developer GUI: you simply have to
replace “null” in their assignment statements with desired actual values
(as both are VARCHAR strings, do not forget the enclosing apo-
strophes!). Figure 2.44 shows STATES instance after inserting North
Dakota.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
114
Figure 2.40: Assigning names to PL/SQL procedure parameters
Figure 2.41: Initially empty Insert_States PL/SQL procedure body
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
115
Figure 2.42: Final Insert_States PL/SQL procedure body
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
116
Figure 2.43: Passing actual parameters values to a PL/SQL procedure
Figure 2.44: STATES instance after inserting state North Dakota
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
117
Note that, both programmatically and from a LAB_DB SQL pane of the
SQL Developer, you can run a PL/SQL procedure and give its actual
parameter values by using the EXECUTE statement; in this case, for
example, you could have run instead:
EXECUTE insert_state ('North Dakota', 'U.S.A.');
2.3 Best practice rules BPR2.0 Before modifying data values, especially for massive updates and
deletes, always backup the corresponding table’s instances (so that if
anything goes wrong you may restore previous values).
BPR2.1 Before modifying data values by using SELECT clauses, espe-
cially for massive modifications, first test corresponding SELECT re-
sults, for checking whether or not they correctly compute the set of desir-
ed rows (be it for insertion, update, or deletion).
BPR2.2 Always strictly modify only data which has to be modified:
don’t forget and program extremely carefully the WHERE clause of the
UPDATE and DELETE statements.
BPR2.3 Even when “nothing can go wrong”, embed data modification
statements in transactions having not only COMMITs, but ROLLBACKs
too: not even RDBMSs are perfect, communications may fail, power ou-
tages may occur, etc.!
BPR2.4 Use the ON DELETE CASCADE predicate with same caution
as when manipulating atomic bombs: it is a potential data “mass-ex-
tinction” weapon!
BPR2.5 Do not forget that, generally, when executing SQL DELETE
statements, RDBMSs do not physically, but only logically delete (in fact,
they mark for deletion) corresponding deleted data; consequently, purge
immediately old unneeded data.
BPR2.6 Always factorize I/O operations, for keeping them to the mini-
mum possible.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
118
BPR2.7 Do not ever deliver any query, view, stored procedure, func-
tion, or trigger without completely and thoroughly unit testing it. All
subsequent tests should be run only with nearly perfectly functioning
units.
2.4 Homework H2.1 Design, develop and test a parameterized query for inserting any ci-
ty in an existing state.
Hint: ask users for city, state, and country names.
H2.2 Parameterize a similar update state capital that has 3 prompts for
country, city, and state and store the fact that corresponding city is the ca-
pital of that state.
H2.3 Insert a test country, 2 test states of it and 4 test cities, 2 per test
state (both manually/SQL and QBE) and then design, develop and test ne-
eded delete statements in order to delete all test data.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
119
Chapter 3. 3rd Lab: INNER JOIN, GROUP BY, and HAVING
clauses
3.1 Introduction Please recall that:
Any time when ambiguities are possible in SQL statements, you
should prefix column names with corresponding table instance
names.
Even when no ambiguities are possible, in ON sub-clauses prefix-
ing of all column names with their corresponding table instance
names is compulsory.
In ORDER BY, ASC is implicit.
Constants should be hard-codded in SQL only exceptionally; ge-
nerally, use parameters instead (e.g. P3.1b below generalizes bil-
lion of queries of the type P3.1a below).
The SQL GROUP BY c1, …, cn clause partitions the set of records
obtained by filtering with the corresponding WHERE clause (if
any) the set of records computed by the corresponding FROM
clause according to the equivalence relation ker(c1 … cn),
where c1, …, cn are columns from that FROM clause table instan-
ces.
Recall that, for any function product f g, ker(f g) = ker(f)
ker(g), and that, for any function f : A B, ker(f) = {(x, y) A2 |
f(x) = f(y)} A2 (called the kernel or nucleus of f).
If you do not give names to your SQL SELECT clause expres-
sions, RDBMSs are assigning automatically generated ones to
them, generally of the type Expr1, Expr2, …
Although there is no standard for what aggregate functions to be
provided, most RDBMSs offer at least the following most fre-
quently used ones: COUNT (for computing set cardinals), SUM,
AVG (for computing arithmetic means), MIN(imum), and MAX(i-
mum).
You cannot compose two SQL aggregate functions (although you
can compose an aggregate function with other library functions).
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
120
The so-called GROUP BY golden rule states that, in the presence
of the GROUP BY clause, corresponding SELECT clause can only
contain columns/expressions listed in the GROUP BY clause
and/or any columns/expressions based on columns of the corres-
ponding FROM clause, provided that they are arguments of aggre-
gate functions.
The order of the SQL SELECT clauses is immutable not only for
syntactical reasons, but for conceptual ones too: it is exactly the
order in which RDBMSs are evaluating these queries.
3.2 The algorithm for assisting DML statements
design Algorithm A0 (DML statements design assistance)
start a(n empty) list with involved tables and columns;
draw the desired result table header;
fill its instance with at least a couple of plausible rows;
Select query type
Case SELECT:
end Case SELECT;
Case INSERT:
end Case INSERT;
Case UPDATE:
end Case UPDATE;
Case DELETE:
end Case DELETE;
end Select query type;
end Algorithm A0 (DML statements design assistance);
procedure determineTargetColumnsList
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
121
repeat for each column
determine whether current column corresponds to a stored or computed
one;
if it is a stored one
then add corresponding stored column and table to both your list and
result table header;
else design and add to the result table header needed computation
formula;
repeat for all stored columns from the formula
add current column and corresponding table to your list;
end repeat;
end repeat for each column;
end procedure determineTargetColumnsList;
procedure determineTargetTableInstancesList
repeat for each table occurrence from the resulted list
if it is not the first occurrence of current table then
determine whether or not you need another instance of current table;
if yes then give it a unique meaningful name else discard it;
end repeat for each table occurence;
repeat for each table instance from the resulted list
repeat for all other table instances from the resulted list
repeat for all foreign keys between them
determine whether or not current foreign key is needed;
if yes then draw a line in your table list between current
foreign key and corresponding referenced column;
end repeat for all foreign keys;
if no line was draw between current table instances then
determine whether or not you need a Cartesian product between
them;
if yes then draw a line between current table instance names
interrupted in the middle by the Cartesian product operator
end repeat for all other table instances;
end repeat for each table instance;
end procedure determineTargetTableInstancesList;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
122
procedure DetermineFilters;
end procedure DetermineFilters;
3.3 Exercises in Access P3.1 a. Compute the set of cities (name, corresponding state and country
names, and population) that have at least 1,000,000 inhabitants, in the
descending order of their population and then ascending on country, state,
and city names.
b. Parameterize a. above and compute result for both 1,000,000
and 500,000.
Solution:
a.
How should the result look like:
Inspecting corresponding data instances, obviously, only three cities qua-
lify for the result (in this order): New York, London, and Bucharest.
The result should then be:
City STATES.State COUNTRIES.Country CITIES.Population
New York New York U.S.A. 8,336,697
London Greater
London
U.K. 8,308,369
Bucharest Bucharest Romania 1,883,425
Data needed for final result: City, CITIES.Population, STATES.State,
and COUNTRIES.Country;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
123
Data needed to link these three tables’ instances: CITIES.State =
STATES.x and STATES.Country = COUNTRIES.x
Data needed for filtering: CITIES.Population
SQL solution:
SELECT City, STATES.State, COUNTRIES.Country,
CITIES.Population
FROM (CITIES INNER JOIN STATES ON CITIES.State = STATES.x)
INNER JOIN COUNTRIES ON STATES.Country = COUNTRIES.x
WHERE CITIES.Population >= 1000000
ORDER BY CITIES.Population DESC, COUNTRIES.Country,
STATES.State, City;
The result of running it against the lab’s db instance is the following:
Figure 3.1 Result of P3.1a
b.
The only difference with respect to the above query is replacing
the hard-codded constant 1000000 with a parameter:
SELECT City, STATES.State, COUNTRIES.Country,
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
124
CITIES.Population
FROM (CITIES INNER JOIN STATES ON CITIES.State = STATES.x)
INNER JOIN COUNTRIES ON STATES.Country = COUNTRIES.x
WHERE CITIES.Population >=
[Please enter desired minimum city population:]
ORDER BY CITIES.Population DESC, COUNTRIES.Country,
STATES.State, City;
Obviously, the result of running it against the lab’s db instance with the
actual parameter value 1000000 is the same as the one in figure 3.1
above.
The result of running it against the lab’s db instance with the actual para-
meter value 500000 (figure 3.2) also selects Chișinău, Memphis, and Wa-
shington (figure 3.3).
Figure 3.2 Entering actual parameter value for P3.1b
Figure 3.3 Result of P3.1b for 500,000
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
125
P3.2 a. Compute the set of countries (name, population, sum of corres-
ponding cities population, unaccounted cities population), in the descend-
ing order of unaccounted cities population, sum of corresponding cities
population, stored countries population, and then ascending on country
names.
b. Same as a. above, but only for countries for which the sum of
cities population is at least equal to a parameter value; run it for 7,000,000
people.
c. Same as b. above, but only for countries whose names start with
‘R’; run it for 2,500,000 people.
Solution:
How should the result look like:
Inspecting corresponding data instances, obviously, all four coun-
tries qualify for the result (in this order): U.S.A., U.K., Romania,
and Moldavia.
The result should then be (where UnaccCityPop = Population -
SumCityPop):
Country Population SumCityPop UnaccCityPop
U.S.A. 316,836,000 9,624,175 307,211,825
U.K. 63,181,775 8,308,369 54873406
Romania 20,121,641 2,604,964 17,516,677
Moldavia 3,559,500 671,800 2887700
Data needed for final result: COUNTRIES.Country, COUNTRIES.Popu-
lation, and CITIES.Population;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
126
Data needed to link these three tables’ instances: CITIES.State =
STATES.x and STATES.Country = COUNTRIES.x
SQL solution:
Both conceptually and from the RDBMSs performance point of view, it is
preferable to split complex problems into smaller and simpler sub-pro-
blems and to interconnect in the end their solutions.
Consequently, let us first solve the sub-problem of computing the sum of
cities populations per countries.
Obviously, by using the SQL aggregate function SUM in the following
query, it computes the sum of all cities populations in the world (see
figure 3.4 for its result):
SELECT SUM(Population) AS TotCitiesPop FROM CITIES;
Figure 3.4 The sum of all cities’ populations
For computing total city populations per country, we obviously need to
partition cities on group per countries, such as for SUM to compute totals
per countries, instead of the worldwide one:
SELECT Sum(CITIES.Population) AS CityPopSum, Country
FROM STATES INNER JOIN CITIES
ON STATES.x = CITIES.State
GROUP BY Country;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
127
Running this query, saved as P3-2-0, against the current lab’s db instance,
it is computing the following result:
Figure 3.5 The sum of all cities’ populations per country
The second sub-problem is to use the results of the previous one for com-
puting final results; obviously, a join of query P3-2-0 with the COUN-
TRIES table is needed in order to get both country names and populations:
SELECT COUNTRIES.Country, COUNTRIES.Population, CityPopSum,
[Population]-[CityPopSum] AS UnaccPop
FROM [P3-2-0] INNER JOIN COUNTRIES
ON [P3-2-0].Country = COUNTRIES.x
ORDER BY [Population]-[CityPopSum] DESC, CityPopSum DESC,
Population DESC, COUNTRIES.Country;
The result of running it against the lab’s db instance is the following:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
128
Figure 3.6 Result of P3.2a
Note that, unfortunately, many programmers would actually come up with
the following equivalent, but not optimal solution:
P3-2-0Bis:
SELECT Sum(CITIES.Population) AS CityPopSum,
COUNTRIES.Country
FROM (STATES INNER JOIN CITIES
ON STATES.x = CITIES.State) INNER JOIN COUNTRIES
ON STATES.Country = COUNTRIES.x
GROUP BY COUNTRIES.Country;
P-3-2aBis: SELECT COUNTRIES.Country, COUNTRIES.Population, CityPopSum,
[Population]-[CityPopSum] AS UnaccPop
FROM [P3-2-0Bis] INNER JOIN COUNTRIES
ON [P3-2-0Bis].Country = COUNTRIES.Country
ORDER BY [Population]-[CityPopSum] DESC, CityPopSum DESC,
Population DESC, COUNTRIES.Country;
Note that P3-2-0B is already taking more time and both memory and disk
space, as it makes an additional join and computes country names (that, in
average, have some 32 ASCII chars) instead of surrogate key values (that
need 4 binary bytes).
Much worse is P-3-2aBis, which is joining not on surrogate key values
(requiring the fastest –arithmetic-logic– unit of the CPU and only one me-
mory cycle per comparison), like P-3-2a, but on ASCII strings (requiring
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
129
the slowest –decimal– unit of the CPU and an average of 32 memory cy-
cles per comparison).
b.
Obviously, the only thing that has to be done is to add a HAVING clause
to P3-2-0:
P3-2-0b:
SELECT Sum(CITIES.Population) AS CityPopSum, STATES.Country
FROM STATES INNER JOIN CITIES ON STATES.x = CITIES.State
GROUP BY STATES.Country
HAVING Sum(CITIES.Population) >= [Please enter desired
minimum cities total population per country:];
Figure 3.7 shows Access’ actual parameter values input window, figures
3.8 – corresponding result of P3-2-0b for 7,000,000, and 3.9 – the one for
the corresponding P-3-2b:
SELECT COUNTRIES.Country, COUNTRIES.Population, CityPopSum,
[Population]-[CityPopSum] AS UnaccPop
FROM [P3-2-0b] INNER JOIN COUNTRIES
ON [P3-2-0b].Country = COUNTRIES.x
ORDER BY [Population]-[CityPopSum] DESC, CityPopSum DESC,
COUNTRIES.Population DESC, COUNTRIES.Country;
Figure 3.7 Entering actual parameter value for P3.2-0b
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
130
Figure 3.8 Result of P3.2-0b for 7,000,000 people
Figure 3.9 Result of P3.2b for 7,000,000 people
Note that a same result may be obtained with a single statement, by using
a subquery (but generally, subqueries are less fast evaluated by RDBMSs
than queries hierarchies):
SELECT COUNTRIES.Country, COUNTRIES.Population, CityPopSum,
[Population]-[CityPopSum] AS UnaccPop
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
131
FROM (SELECT Sum(CITIES.Population) AS CityPopSum,
STATES.Country
FROM STATES INNER JOIN CITIES
ON STATES.x = CITIES.State
GROUP BY STATES.Country
HAVING Sum(CITIES.Population) >= [Please enter
desired minimum cities total population per
country:]) AS [P3-2-0b]
INNER JOIN COUNTRIES ON [P3-2-0b].Country = COUNTRIES.x
ORDER BY [Population]-[CityPopSum] DESC, CityPopSum DESC,
COUNTRIES.Population DESC, COUNTRIES.Country;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
132
c.
Even if not that obvious, the best thing to do is to add a corresponding
filter to P-3-2-0b:
P-3-2-0c:
SELECT Sum(CITIES.Population) AS CityPopSum, STATES.Country
FROM COUNTRIES INNER JOIN (STATES INNER JOIN CITIES
ON STATES.x = CITIES.State)
ON STATES.Country = COUNTRIES.x
WHERE COUNTRIES.Country Like "R*"
GROUP BY STATES.Country
HAVING Sum(CITIES.Population) >= [Please enter desired
minimum cities total population per country:];
Figure 3.10 shows Access’ actual parameter values input window, figures
3.11 – corresponding result of P3-2-0c for 2,500,000, and 3.12 – the one
for the corresponding P-3-2c:
SELECT COUNTRIES.Country, COUNTRIES.Population, CityPopSum,
[Population]-[CityPopSum] AS UnaccPop
FROM [P3-2-0c] INNER JOIN COUNTRIES
ON [P3-2-0c].Country = COUNTRIES.x
ORDER BY [Population]-[CityPopSum] DESC, CityPopSum DESC,
COUNTRIES.Population DESC, COUNTRIES.Country;
Figure 3.10 Entering actual parameter value for P3.2-0c
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
133
Figure 3.11 Result of P3.2-0c for 2,500,000 people
Figure 3.12 Result of P3.2c for 2,500,000 people
Please note again that, unfortunately, some programmers would rather
come up with one of the following equivalent, but not at all optimal
solutions:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
134
P-3-2-0cBis:
SELECT Sum(CITIES.Population) AS CityPopSum, STATES.Country
FROM COUNTRIES INNER JOIN (STATES INNER JOIN CITIES
ON STATES.x = CITIES.State)
ON STATES.Country = COUNTRIES.x
GROUP BY STATES.Country
HAVING COUNTRIES.Country Like "R*" AND
Sum(CITIES.Population) >= [Please enter desired
minimum cities total population per country:];
or keeping P-3-2-0b as such and modifying P-3-2c to:
P-3-2cBis:
SELECT COUNTRIES.Country, COUNTRIES.Population, CityPopSum,
[Population]-[CityPopSum] AS UnaccPop
FROM [P3-2-0cBis] INNER JOIN COUNTRIES
ON [P3-2-0cBis].Country = COUNTRIES.x
WHERE COUNTRIES.Country Like "R*"
ORDER BY [Population]-[CityPopSum] DESC, CityPopSum DESC,
COUNTRIES.Population DESC, COUNTRIES.Country;
When you compare them, it is trivial that:
P3-2-0c is only computing, in this particular case, one group (for
Romania) and, generally, about one dozen group (for Romania,
Russia, Rwanda, etc.), whereas
Both P-3-2-0cBis and P-3-2cBis are still computing all groups
(four in this particular case, but some 250 for full countries’ data)
and then are throwing away the vast majority of their computation
results (three groups in this particular case, but some 238 for full
countries’ data).
Generally, note that we cannot get rid of HAVING clauses, as this is the
only place where we can add filters on data computed (generally through
aggregation) after grouping; dually, we could sometimes get rid of
WHERE clauses (not always, as –see figure 3.4 above– sometimes we
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
135
might need filters on global applications of aggregate functions) and only
use HAVING ones, but this would be a stupid thing to do, both concep-
tually and, especially, performance-wise.
3.4 Exercises in Oracle P3.1 a. Compute the set of cities (name, corresponding state and country
names, and population) that have at least 1,000,000 inhabitants, in the
descending order of their population and then ascending on country, state,
and city names.
b. Parameterize a. above and compute results for both 1,000,000
and 500,000.
Solution:
a.
How should the result look like:
Inspecting corresponding data instances, obviously, only three cities qua-
lify for the result (in this order): New York, London, and Bucharest.
The result should then be:
City STATES.State COUNTRIES.Country CITIES.Population
New York New York U.S.A. 8,336,697
London Greater
London
U.K. 8,308,369
Bucharest Bucharest Romania 1,883,425
Data needed for final result: City, CITIES.Population, STATES.State,
and COUNTRIES.Country;
Data needed to link these three tables’ instances: CITIES.State =
STATES.x and STATES.Country = COUNTRIES.x
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
136
Data needed for filtering: CITIES.Population
SQL solution:
SELECT City, STATES.State, COUNTRIES.Country,
CITIES.Population
FROM (CITIES INNER JOIN STATES ON CITIES.State = STATES.x)
INNER JOIN COUNTRIES ON STATES.Country = COUNTRIES.x
WHERE CITIES.Population >= 1000000
ORDER BY CITIES.Population DESC, COUNTRIES.Country,
STATES.State, City;
The result of running it against the lab’s db instance is the following:
Figure 3.13 Result of P3.1a
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
137
b.
The only difference with respect to the above query is replacing
the hard-codded constant 1000000 with a parameter:
SELECT City, STATES.State, COUNTRIES.Country,
CITIES.Population
FROM (CITIES INNER JOIN STATES ON CITIES.State = STATES.x)
INNER JOIN COUNTRIES ON STATES.Country = COUNTRIES.x
WHERE CITIES.Population >=
:Minimum_city_population
ORDER BY CITIES.Population DESC, COUNTRIES.Country,
STATES.State, City;
Obviously, the result of running it against the lab’s db instance with the
actual parameter value 1000000 is the same as the one in figure 3.13
above.
The result of running it against the lab’s db instance with the actual para-
meter value 500000 (figure 3.14) also selects Chișinău, Memphis, and
Washington (figure 3.15). Note that Oracle variable names can be of at
most 30 chars and cannot contain spaces.
Figure 3.14 Entering actual parameter value for P3.1b
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
138
Figure 3.15 Result of P3.1b for 500,000
P3.2 a. Compute the set of countries (name, population, sum of corres-
ponding cities population, unaccounted cities population), in the descend-
ing order of unaccounted cities population, sum of corresponding cities
population, stored countries population, and then ascending on country
names.
b. Same as a. above, but only for countries for which the sum of
cities population is at least equal to a parameter value; run it for 7,000,000
people.
c. Same as b. above, but only for countries whose names start with
‘R’; run it for 2,500,000 people.
Solution:
How should the result look like:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
139
Inspecting corresponding data instances, obviously, all four coun-
tries qualify for the result (in this order): U.S.A., U.K., Romania,
and Moldavia.
The result should then be (where UnaccCityPop = Population -
SumCityPop):
Country Population SumCityPop UnaccCityPop
U.S.A. 316,836,000 9,624,175 307,211,825
U.K. 63,181,775 8,308,369 54873406
Romania 20,121,641 2,604,964 17,516,677
Moldavia 3,559,500 671,800 2887700
Data needed for final result: COUNTRIES.Country, COUNTRIES.Popu-
lation, and CITIES.Population;
Data needed to link these three tables’ instances: CITIES.State =
STATES.x and STATES.Country = COUNTRIES.x
SQL solution:
Both conceptually and from the RDBMSs performance point of view, it is
preferable to split complex problems into smaller and simpler sub-pro-
blems and to interconnect in the end their solutions.
Consequently, let us first solve the sub-problem of computing the sum of
cities populations per countries.
Obviously, by using the SQL aggregate function SUM in the following
query, it computes the sum of all cities populations in the world (see
figure 3.4 for its result):
SELECT SUM(Population) AS TotCitiesPop FROM CITIES;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
140
Figure 3.16 The sum of all cities’ populations
For computing total city populations per country, we obviously need to
partition cities on group per countries, such as for SUM to compute totals
per countries, instead of the worldwide one:
SELECT Sum(CITIES.Population) AS CityPopSum, Country
FROM STATES INNER JOIN CITIES
ON STATES.x = CITIES.State
GROUP BY Country;
Running this query against the current lab’s db instance is computing the
following result:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
141
Figure 3.17 The sum of all cities’ populations per country
In order to make use of it in the final step, you should save this query as
view P3-2-0; right-click the View node of LAB_DB, then click on New
View (figure 3.18); in the Create View window that pops up (figure 3.19),
enter the Name of the view (that should be distinct from names of any
other tables and views of LAB_DB) and copy the statement in the SQL
Query text box; click on the Check Syntax button: the message “SQL
Parse Results: No errors found in SQL” should be displayed in the bot-
tom-left corner of the window; click on the Test Query button: the Test
Query window that pops up (figure 3.20) should display the “Query exe-
cuted successfully” Result; click on Close and then on the OK button of
the Create View (figure 3.19): your view is saved and ready to be used.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
142
Figure 3.18 Creating a new view in Oracle SQL Developer
Figure 3.19 Naming and specifying a view in Oracle SQL Developer
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
143
Figure 3.20 Testing a view in Oracle SQL Developer
Note that the same result could have been obtained by running the follow-
ing DDL statement:
--------------------------------------------------------
-- DDL for View P3_2_0
--------------------------------------------------------
CREATE OR REPLACE FORCE VIEW "LAB_DB"."P3_2_0"
("CITYPOPSUM", "COUNTRY") AS
SELECT SUM(CITIES.POPULATION) AS CityPopSum, COUNTRY
FROM STATES INNER JOIN CITIES ON STATES.X = CITIES.STATE
GROUP BY COUNTRY;
The second sub-problem is to use the results of the previous one for com-
puting final results; obviously, a join of view P3-2-0 with the COUN-
TRIES table is needed in order to get both country names and populations:
SELECT COUNTRIES.Country, COUNTRIES.Population, CityPopSum,
Population - CityPopSum AS UnaccPop
FROM P3_2_0 INNER JOIN COUNTRIES
ON P3_2_0.Country = COUNTRIES.x
ORDER BY Population - CityPopSum DESC, CityPopSum DESC,
Population DESC, COUNTRIES.Country;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
144
The result of running it against the lab’s db instance is the following:
Figure 3.21 Result of P3.2a
Note that, unfortunately, many programmers would actually come up with
the following equivalent, but not optimal solution:
P3_2_0Bis:
SELECT Sum(CITIES.Population) AS CityPopSum,
COUNTRIES.Country
FROM (STATES INNER JOIN CITIES
ON STATES.x = CITIES.State) INNER JOIN COUNTRIES
ON STATES.Country = COUNTRIES.x
GROUP BY COUNTRIES.Country;
P_3_2aBis: SELECT COUNTRIES.Country, COUNTRIES.Population, CityPopSum,
Population - CityPopSum AS UnaccPop
FROM P3_2_0Bis INNER JOIN COUNTRIES
ON P3_2_0Bis.Country = COUNTRIES.Country
ORDER BY Population - CityPopSum DESC, CityPopSum DESC,
Population DESC, COUNTRIES.Country;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
145
Note that P3_2_0B is already taking more time and both memory and
disk space, as it makes an additional join and computes country names
(that, in average, have some 32 ASCII chars) instead of surrogate key va-
lues (that need 4 binary bytes).
Much worse is P_3_2aBis, which is joining not on surrogate key values
(requiring the fastest –arithmetic-logic– unit of the CPU and only one me-
mory cycle per comparison), like P_3_2a, but on ASCII strings (requiring
the slowest –decimal– unit of the CPU and an average of 32 memory cy-
cles per comparison).
b.
Obviously, the only thing that has to be done is to add a HAVING clause
to P3_2_0; here is the corresponding P3_2_0b:
SELECT Sum(CITIES.Population) AS CityPopSum, STATES.Country
FROM STATES INNER JOIN CITIES ON STATES.x = CITIES.State
GROUP BY STATES.Country
HAVING Sum(CITIES.Population) >=
:Min_city_tot_pop_per_country;
Figure 3.22 shows Oracle’s actual parameter values input window,
figures 3.23 – corresponding result of P3_2_0b for 7,000,000, and 3.24 –
the one for the corresponding P_3_2b (with P_3_2_0b as a subquery):
SELECT COUNTRIES.Country, COUNTRIES.Population, CityPopSum,
Population - CityPopSum AS UnaccPop
FROM (SELECT Sum(CITIES.Population) AS CityPopSum,
STATES.Country
FROM STATES INNER JOIN CITIES
ON STATES.x = CITIES.State
GROUP BY STATES.Country
HAVING Sum(CITIES.Population) >=
:Min_city_tot_pop_per_country) P3_2_0b
INNER JOIN COUNTRIES ON P3_2_0b.Country = COUNTRIES.x
ORDER BY Population - CityPopSum DESC, CityPopSum DESC,
COUNTRIES.Population DESC, COUNTRIES.Country;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
146
Figure 3.22 Entering actual parameter value for P3_2_0b
Figure 3.23 Result of P3_2_0b for 7,000,000 people
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
147
Figure 3.24 Result of P3_2b for 7,000,000 people
c.
Even if not that obvious, the best thing to do is to add a corresponding
filter to P_3_2_0b:
P_3_2_0c:
SELECT Sum(CITIES.Population) AS CityPopSum, STATES.Country
FROM COUNTRIES INNER JOIN (STATES INNER JOIN CITIES
ON STATES.x = CITIES.State)
ON STATES.Country = COUNTRIES.x
WHERE COUNTRIES.Country Like ‘R%’
GROUP BY STATES.Country
HAVING Sum(CITIES.Population) >=
:Min_city_tot_pop_per_country;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
148
Figure 3.25 shows Oracle’ actual parameter values input window, figures
3.26 – corresponding result of P3_2_0c for 2,500,000, and 3.27 – the one
for the corresponding P_3_2c:
SELECT COUNTRIES.Country, COUNTRIES.Population, CityPopSum,
Population - CityPopSum AS UnaccPop
FROM (SELECT Sum(CITIES.Population) AS CityPopSum,
STATES.Country
FROM COUNTRIES INNER JOIN (STATES INNER JOIN CITIES
ON STATES.x = CITIES.State)
ON STATES.Country = COUNTRIES.x
WHERE COUNTRIES.Country Like ‘R%’
GROUP BY STATES.Country
HAVING Sum(CITIES.Population) >=
:Min_city_tot_pop_per_country)
P3_2_0c INNER JOIN COUNTRIES
ON P3_2_0c.Country = COUNTRIES.x
ORDER BY Population - CityPopSum DESC, CityPopSum DESC,
COUNTRIES.Population DESC, COUNTRIES.Country;
Figure 3.25 Entering actual parameter value for P3_2_0c
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
149
Figure 3.26 Result of P3_2_0c for 2,500,000 people
Figure 3.27 Result of P3_2c for 2,500,000 people
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
150
Unfortunately, Oracle does not accept parameterized views; consequent-
ly, the only way to store and run parameterized queries are the PL/SQL
stored procedures; as it is best to group all such procedures addressing
some same functional specifications in a PL/SQL package, let us create
such a package. Right-click the Packages node of LAB_DB and then
click on New Package:
Figure 3.28 Creating a new PL/SQL package
In the Create PL/SQL Package window that pops up, enter desired pac-
kage name:
Figure 3.29 Naming a new PL/SQL package
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
151
In the header of the newly created package, replace the comment /* To do
… */ with the following two declarations (see figure 3.30):
TYPE GenericCursorType IS REF CURSOR;
procedure p3_2c (min_city_tot_pop_per_country number,
rc OUT GenericCursorType);
Figure 3.30 The header of the LAB_DB_SQL PL/SQL package
For creating the package body, right-click on the package’s name and
then click on Create Body…:
Figure 3.31 Creating the body of the LAB_DB_SQL PL/SQL package
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
152
In the newly created body, enter procedure’s P3_2c definition (see figure
3.32):
procedure p3_2c
(
Min_city_tot_pop_per_country in number,
rc out GenericCursorType
) is
begin
open rc for
SELECT COUNTRIES.Country, COUNTRIES.Population,
CityPopSum, Population - CityPopSum AS UnaccPop
FROM (SELECT Sum(CITIES.Population) AS CityPopSum,
STATES.Country
FROM COUNTRIES INNER JOIN (STATES INNER JOIN CITIES
ON STATES.x = CITIES.State)
ON STATES.Country = COUNTRIES.x
WHERE COUNTRIES.Country Like 'R%'
GROUP BY STATES.Country
HAVING Sum(CITIES.Population) >=
Min_city_tot_pop_per_country)
P3_2_0c INNER JOIN COUNTRIES
ON P3_2_0c.Country = COUNTRIES.x
ORDER BY Population - CityPopSum DESC, CityPopSum DESC,
COUNTRIES.Population DESC, COUNTRIES.Country;
end p3_2c;
In order to run this packaged procedure with desired parameters, enter in
a LAB_DB SQL tab the following statements:
var c refcursor;
exec lab_db_pl_sql.p3_2c(2500000, :c);
print c;
Running them (see figure 3.33), you get same results as in figure 3.26
above; the main advantage with this approach is that you can obtain and
then process this result from now on programmatically too (e.g. in VBA,
Java, .NET, etc.).
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
153
Figure 3.32 The body of the LAB_DB_SQL PL/SQL package
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
154
Figure 3.33 Running the LAB_DB_SQL.P3_2C PL/SQL packaged
procedure
Please note again that, unfortunately, some programmers would rather
come up with one of the following equivalent, but not at all optimal solu-
tions:
P_3_2_0cBis:
SELECT Sum(CITIES.Population) AS CityPopSum, STATES.Country
FROM COUNTRIES INNER JOIN (STATES INNER JOIN CITIES
ON STATES.x = CITIES.State)
ON STATES.Country = COUNTRIES.x
GROUP BY STATES.Country
HAVING COUNTRIES.Country Like ‘R%’ AND
Sum(CITIES.Population) >= : Min_city_tot_pop_per_country;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
155
or keeping P_3_2_0c as such and modifying P_3_2c to:
P_3_2cBis:
SELECT COUNTRIES.Country, COUNTRIES.Population, CityPopSum,
Population - CityPopSum AS UnaccPop
FROM (SELECT Sum(CITIES.Population) AS CityPopSum,
STATES.Country
FROM COUNTRIES INNER JOIN (STATES INNER JOIN CITIES
ON STATES.x = CITIES.State)
ON STATES.Country = COUNTRIES.x
GROUP BY STATES.Country
HAVING Sum(CITIES.Population) >=
:Min_city_tot_pop_per_country)
P3_2_0c INNER JOIN COUNTRIES
ON P3_2_0c.Country = COUNTRIES.x
WHERE COUNTRIES.Country Like ‘R%’
ORDER BY Population - CityPopSum DESC, CityPopSum DESC,
COUNTRIES.Population DESC, COUNTRIES.Country;
When you compare them, it is trivial that:
P3_2_0c is only computing, in this particular case, one group (for
Romania) and, generally, about one dozen group (for Romania,
Russia, Rwanda, etc.), whereas
Both P_3_2_0cBis and P_3_2cBis are still computing all groups
(four in this particular case, but some 250 for full countries’ data)
and then are throwing away the vast majority of their computation
results (three groups in this particular case, but some 238 for full
countries’ data).
Generally, note that we cannot get rid of HAVING clauses, as this is the
only place where we can add filters on data computed (generally through
aggregation) after grouping; dually, we could sometimes get rid of
WHERE clauses (not always, as –see figure 3.4 above– sometimes we
might need filters on global applications of aggregate functions) and only
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
156
use HAVING ones, but this would be a stupid thing to do, both concep-
tually and, especially, performance wise.
3.5 Best practice rules BPR3.0 Always name your SQL SELECT clause expressions with
proper names (by using the AS renaming operator).
BPR3.1 Both conceptually and from the RDBMSs performance point of
view, it is preferable to split complex problems into smaller and sim-
pler sub-problems and to interconnect in the end their solutions.
BPR3.2 Always use only necessary data (dually: never use
unnecessary tables and/or columns) in your queries.
BPR3.3 When possible, always join table instances on smallest nume-
rical keys (generally, primary surrogate ones), instead of any other
existing equivalent keys.
BPR3.4 Always use WHERE for filtering as much as possible before
grouping.
BPR3.5 Use HAVING only for filtering on data computed after
grouping (dually: never use HAVING for filters that can be placed on
WHERE!).
BPR3.6 Never present users with unordered results, except for cases
when they are explicitly asking for it.
BPR3.7 Always order results intelligently, such as to maximize users
experience with your application.
BPR3.8 Never order data more than once, in the final querying step.
BPR3.9 Never order on more columns/expressions than needed: or-
dering costs a lot!
BPR3.10 Never order by using column positions! For example, always
use SELECT x, y … ORDER BY x, y; never use SELECT x, y
… ORDER BY 1, 2; instead, as, one day, when you will have to
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
157
change it to SELECT y, x … ORDER BY x, y;, you have to also
change the ordering order (to SELECT y, x … ORDER BY x, y;) .
3.5 Homework H3.0 Prove that:
a. the functions kernel relation is an equivalence one.
b. the kernel of a function product is equal to the intersection of
involved kernels
H3.1 Prove that there is no SQL solution for P3.2 above without subque-
ries or queries hierarchies.
Hint: consider both the “GROUP BY golden rule” and the restriction that
aggregate functions cannot be composed between them.
H3.2 Compute the set of countries having at least k states, each of which
has at least n cities (k and n being natural parameters), for which the unac-
counted states and cities population per countries are at least equal to
other two distinct parameters, respectively, in the descending order of the
unaccounted states population per country, city population per country,
corresponding accounted ones, stored countries population, and then as-
cending on country name.
p.s. It is highly possible that an exercise of this type, generally simpler
from the arithmetic point of view, be the main oral examination subject at
the end of this semester!
H3.3 a. Add to the COUNTRIES table data for Hungary, Serbia, Bulgaria,
Greece, Malta, and Ukraine.
b. Add to your lab db a table for storing the NEIGHBORS binary
relation defined over COUNTRIES: NEIGHBORS = { (x,y) COUN-
TRIES2 |x is neighbor to y } and populate it with actual data for all
countries in COUNTRIES.
H3.4 Translate into relational algebra and optimize all the SELECT state-
ments from these first three DB labs.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
158
Chapter 4. 4th Lab: Outer Joins, the Is Null predicate,
and UNIONs
4.1 Prerequisites in Access Homework 3.3 is the pre-requisite for this lab.
4.1.1 Adding needed countries to the COUNTRIES table
COUNTRIES should have the following instance:
Figure 4.1 COUNTRIES instance after solving homework 3.3a
4.1.2 Adding the NEIGHBORS table and populating its instance
Obviously, the RDM scheme and corresponding instance should be the
one presented in figure 4.2; figures 4.3 and 4.4 show its corresponding
Access implementation and instance.
Note that, trivially, Country Neighbor is a key, as it wouldn’t make
sense to store more than once any neighboring fact. Moreover, obviously,
from a db perspective, this mathematical relation is irreflexive (as it is
senseless to store the fact that a country is neighbor to itself) and antisym-
metric (as, once having stored that country c is neighbor to country n, the
dual fact that country n is neighbor to country c is computable, so it does
not make sense to store it too). Unfortunately, no RDBMS can enforce
these constraints (as they are non-relational, so not expressible in pure
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
159
SQL), but only through triggers or trigger-type methods (using either
extended SQL or high level programming languages – Java, C#, VBA, ...).
NEIGHBORS (x, Country Neighbor)
x Country Neighbor
autonumber(4) Im(COUNTRIES.x) Im(COUNTRIES.x)
NOT NULL NOT NULL NOT NULL
1 1 2
2 1 5
3 1 6
4 1 9
5 1 10
6 2 9
7 5 6
8 5 10
9 6 7
10 9 10
Figure 4.2 NEIGHBORS table (see homework 3.3b)
Figure 4.3 Access implementation of the NEIGHBORS table
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
160
Figure 4.4 Access instance of the NEIGHBORS table
4.2 Exercises in Access P4.1. a. Compute the number of neighbors of country ‘Moldavia’.
b. Parameterize this query and compute the numbers of neighbors
of country ‘Serbia’.
Solution:
a.
Firstly, please note that, due to NEIGHBORS antisymmetry, any country
may appear in this table instance either only in the Country column (e.g.
Romania), or only in the Neighbor one (e.g. Hungary), or in both of them
(e.g. Serbia, Bulgaria, Ukraine).
Trivially, the only table we need is NEIGHBORS, but, generally, for any
country, we have to add the number of its occurrences in both columns
(corresponding to this math relation canonical Cartesian projections).
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
161
Consequently, the SQL solution (save it as query P4-1) is the following:
SELECT COUNT(*) AS MoldaviaNeighborNo FROM NEIGHBORS
WHERE COUNTRY =
DLookup("x", "COUNTRIES", "Country = 'Moldavia'")
OR NEIGHBOR =
DLookup("x", "COUNTRIES", "Country = 'Moldavia'");
The equivalent Design mode (QBE) solution is:
Figure 4.5 Access solution to P4-1
Please note first that this is the fastest possible solution: Access only eva-
luates once the DLookup (as it has same actual parameter values in both
calls); the only faster solution is when we know the corresponding coun-
try code (as no access to COUNTRIES is needed anymore):
SELECT COUNT(*) AS MoldaviaNeighborNo FROM NEIGHBORS
WHERE COUNTRY = 2 OR NEIGHBOR = 2;
Another equivalent, more elegant, but less performing solution involves
using subqueries instead of the DLookup function (which also has a di-
dactic advantage: it provides yet another example of the DLookup seman-
tics; note that the TOP predicate is not needed here, as Country is a key in
COUNTRIES):
SELECT COUNT(*) AS MoldaviaNeighborNo FROM NEIGHBORS
WHERE COUNTRY =
(SELECT x FROM COUNTRIES WHERE Country = 'Moldavia')
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
162
OR NEIGHBOR =
(SELECT x FROM COUNTRIES WHERE Country = 'Moldavia');
The following equivalent solution, which replaces subqueries with a hie-
rarchies of queries is only a very little bit faster, but still slower than the
P4-1 above (the one which also uses DLookup, but in only one step), be-
cause it is evaluating twice same DLookup call:
P4-1CBis:
SELECT COUNT(*) AS MoldaviaNeighborNo FROM NEIGHBORS
WHERE COUNTRY =
DLookup("x", "COUNTRIES", "Country = 'Moldavia'");
P4-1NBis:
SELECT COUNT(*) AS MoldaviaNeighborNo FROM NEIGHBORS
WHERE NEIGHBOR =
DLookup("x", "COUNTRIES", "Country = 'Moldavia'");
P4-1Bis:
SELECT P4-1aCBis.MoldaviaNeighborNo +
P4-1aNBis.MoldaviaNeighborNo AS MoldaviaNeighborNo
FROM P4-1aCBis, P4-1aNBis;
Note that if irreflexivity is not enforced, then any row of type <x,x>
would vitiate this result (as compared to the mental corresponding com-
putation): for example, if the table instance would also contain line <11,
2, 2>, then (because of the OR), SQL computes 3, whereas there are 4 oc-
currences of 2 (two in Countries and 2 in Neighbors). Obviously, this is
just another reason for enforcing irreflexivity of NEIGHBORS too.
b.
Parameterization of P4-1a is straightforward (save it as P-41b):
SELECT COUNT(*) AS CountryNeighborNo FROM NEIGHBORS
WHERE COUNTRY =
DLookup("x", "COUNTRIES", "Country = '” &
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
163
[Please enter desired country name:] & “'")
OR NEIGHBOR =
DLookup("x", "COUNTRIES", "Country = '” &
[Please enter desired country name:] & “'");
Figure 4.6 shows the window for passing “Serbia” as an actual parameter
value to it, whereas 4.7 presents its result for this value:
Figure 4.6 Passing an actual parameter value to query P4-1b
Figure 4.7 Query P4-1b result for “Serbia”
P4.2 Compute the set of countries without neighbors, in ascending order
of their names.
Solution:
Obviously, all needed data is stored in COUNTRIES and NEIGHBORS
and, again, due to NEIGHBORS math relation antisymmetry, we have to
look up for such countries in both Country and Neighbor columns.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
164
Unfortunately, as Access does not accept unions in subqueries, the
following simplest and most elegant SQL solution cannot be used:
SELECT COUNTRY FROM COUNTRIES WHERE X NOT IN
(SELECT DISTINCT COUNTRY FROM NEIGHBORS
UNION
SELECT DISTINCT NEIGHBOR FROM NEIGHBORS)
ORDER BY COUNTRY;
Consequently, we have to modify it slightly (save it as P4-2):
SELECT COUNTRY FROM COUNTRIES
WHERE x NOT IN (SELECT DISTINCT COUNTRY FROM NEIGHBORS)
AND x NOT IN (SELECT DISTINCT NEIGHBOR FROM NEIGHBORS)
ORDER BY COUNTRY;
Figure 4.8 shows its result; note that, just like in math, the SQL UNION
operator eliminates duplicates13
.
Figure 4.8 Query P4-2 result
A faster, subqueryless equivalent statement makes use of outer joins, the
Is Null predicate, and a queries hierarchy (save it as P4-2Bis) based on
two dual preliminary queries (P4-2CBis and P4-2NBis):
13
Whenever you need such duplicates, use UNION ALL instead.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
165
P4-2CBis:
SELECT DISTINCT Country AS x FROM NEIGHBORS;
P4-2NBis:
SELECT DISTINCT Neighbor AS x FROM NEIGHBORS;
P4-2Bis:
SELECT Country
FROM (COUNTRIES LEFT JOIN [P4-2CBis]
ON COUNTRIES.x = [P4-2CBis].x) LEFT JOIN
[P4-2NBis] ON COUNTRIES.x = [P4-2NBis].x
WHERE [P4-2CBis].x Is Null AND [P4-2NBis].x Is Null
ORDER BY COUNTRIES.Country;
Note that without DISTINCT, the result is the same, but it takes more
time to be computed: for example, P4-2CBis above computes the set {1,
2, 5, 6, 9} (see figure 4.9), whereas SELECT Country AS x FROM
NEIGHBORS; is computing the list {1, 1, 1, 1, 1, 2, 5, 5, 6, 9}.
Figure 4.9 Result of running P4-2CBis
Also note that the following simpler, one step equivalent query, is the fas-
test of them all, as it does not use either subqueries or queries hierarchies,
but only two instances of table NEIGHBORS:
SELECT DISTINCT COUNTRIES.Country
FROM (COUNTRIES LEFT JOIN NEIGHBORS
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
166
ON COUNTRIES.x = NEIGHBORS.Country) LEFT JOIN
NEIGHBORS AS NEIGHBOR_COUNTRIES
ON COUNTRIES.x = NEIGHBOR_COUNTRIES.Neighbor
WHERE NEIGHBORS.Country Is Null AND
NEIGHBOR_COUNTRIES.Neighbor Is Null
ORDER BY COUNTRIES.Country;
P4.3. a. Compute the set of pairs <country name, neighbors number> for
countries having at least k neighbors (k natural), in descending order of
the number of their neighbors and then ascending on their names.
b. What are the corresponding results for k = 3 and k = 0?
Solution:
Obviously, the corresponding result for any k 0, manually computed (by
writing all countries from COUNTRIES, in alphabetic order, in the first
column of the table shown in figure 4.10, counting how many times each
one appears in NEIGHBOR.Country and writing the result in the Right-
Neighbors column, then same thing for the NEIGHBOR.Neighbor and
writing the result in the LeftNeighbors one, and then adding the two pre-
vious column values and writing them down in the TotalNeighbors co-
lumn), is the following (but only in the alphabetic order of Country, not in
the one asked by the exercise):
Country RightNeighbors LeftNeighbors TotalNeighbors
Bulgaria 1 2 3
Greece 0 1 1
Hungary 0 3 3
Malta 0 0 0
Moldavia 1 1 2
Romania 5 0 5
Serbia 2 1 3
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
167
U.K. 0 0 0
U.S.A. 0 0 0
Ukraine 1 2 3
Figure 4.10 Results of manually computing total number of neighbors per
country (in the ascending order of country names)
As even manually this problem is sub-dividable in smaller problems, let’s
program it in SQL in the same manner, step by step:
P4-3a0C (compute total number of neighbors per country for Country):
SELECT Country, Count(Neighbor) AS [N#]
FROM NEIGHBORS GROUP BY Country;
P4-3a0N (compute total number of neighbors per country for Neighbor):
SELECT Neighbor, Count(Country) AS [N#]
FROM NEIGHBORS GROUP BY Neighbor;
P4-3a1B (compute total number of neighbors per country for countries
occurring both in Country and Neighbor):
SELECT Country,
[P4-3a0c].[N#]+[P4-3a0n].[N#] AS NeighborsInBothSides
FROM [P4-3a0C] INNER JOIN [P4-3a0N]
ON [P4-3a0C].Country = [P4-3a0N].Neighbor;
P4-3a1L (compute total number of neighbors per country for countries
occurring only in Country):
SELECT Country, [P4-3a0C].[N#]
FROM [P4-3a0C] LEFT JOIN [P4-3a0N]
ON [P4-3a0C].Country = [P4-3a0N].Neighbor
WHERE Neighbor Is Null;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
168
P4-3a1R (compute total number of neighbors per country for countries
occurring only in Neighbor):
SELECT Neighbor, [P4-3a0N].[N#]
FROM [P4-3a0N] LEFT JOIN [P4-3a0C]
ON [P4-3a0N].Neighbor = [P4-3a0C].Country
WHERE Country Is Null;
P4-3a2 (union results of P4-2, for countries with no neighbors, slightly
modified in order to select x instead of Country,with the ones of P4-3a1B,
P4-3a1L, and P4-3a1R):
SELECT x, 0 AS NeighborsNo FROM COUNTRIES
WHERE x NOT IN
(SELECT DISTINCT COUNTRY FROM NEIGHBORS)
AND x NOT IN
(SELECT DISTINCT NEIGHBOR FROM NEIGHBORS)
UNION
SELECT * FROM [P4-3a1B]
UNION
SELECT * FROM [P4-3a1L]
UNION
SELECT * FROM [P4-3a1R];
P4-3a (computing final result):
SELECT Country, NeighborsNo
FROM COUNTRIES INNER JOIN [P4-3a2]
ON COUNTRIES.x = [P4-3a2].x
WHERE NeighborsNo >= [k:]
ORDER BY NeighborsNo DESC, COUNTRIES.Country;
Figures 4.11 and 4.12 show the corresponding results for k = 3 and k = 0:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
169
Figure 4.11 Result of P4-3a for k = 3
Figure 4.12 Result of P4-3a for k = 0
Again, just like for P4-2 above, the following simpler solution is not ac-
cepted by Access, as it uses UNION in a subquery:
SELECT COUNTRIES.COUNTRY, [NEIGHBORS#]
FROM COUNTRIES INNER JOIN
(SELECT COUNTRY, SUM ([N#]) AS [NEIGHBORS#] FROM
(SELECT COUNTRY, COUNT(*) AS [N#]
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
170
FROM NEIGHBORS GROUP BY COUNTRY
UNION
SELECT NEIGHBOR, COUNT(*) AS [N#]
FROM NEIGHBORS GROUP BY NEIGHBOR
UNION
SELECT x, 0 AS NeighborsNo FROM COUNTRIES
WHERE x NOT IN
(SELECT DISTINCT COUNTRY FROM NEIGHBORS)
AND x NOT IN
(SELECT DISTINCT NEIGHBOR FROM NEIGHBORS))
GROUP BY COUNTRY
HAVING SUM ([N#]) >= [k:]) AS S
ON COUNTRIES.X = S.COUNTRY
ORDER BY [NEIGHBORS#] DESC, COUNTRIES.COUNTRY;
4.3 Prerequisites in Oracle
Homework 3.3 is the pre-requisite for this lab.
4.1.1 Adding needed countries to the COUNTRIES table
COUNTRIES should have the following instance:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
171
Figure 4.13 COUNTRIES instance after solving homework 3.3a
4.1.2 Adding the NEIGHBORS table and populating its instance
Obviously, the RDM scheme and corresponding instance should be the
one presented in figure 4.14; figures 4.15 and 4.16 show its corresponding
Oracle implementation and instance.
Note that, trivially, Country Neighbor is a key, as it wouldn’t make
sense to store more than once any neighboring fact. Moreover, obviously,
from a db perspective, this mathematical relation is irreflexive (as it is
senseless to store the fact that a country is neighbor to itself) and antisym-
metric (as, once having stored that country c is neighbor to country n, the
dual fact that country n is neighbor to country c is computable, so it does
not make sense to store it too). Unfortunately, no RDBMS can enforce
these constraints (as they are non-relational, so not expressible in pure
SQL), but only through triggers or trigger-type methods (using either
extended SQL or high level programming languages – Java, C#, VBA, ...).
NEIGHBORS (x, Country Neighbor)
x Country Neighbor
autonumber(4) Im(COUNTRIES.x) Im(COUNTRIES.x)
NOT NULL NOT NULL NOT NULL
1 1 2
2 1 25
3 1 26
4 1 29
5 1 30
6 2 29
7 25 26
8 25 30
9 26 27
10 29 30
Figure 4.14 NEIGHBORS table (see homework 3.3b)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
172
Figure 4.15 Oracle implementation of the NEIGHBORS table
Figure 4.16 Oracle instance of the NEIGHBORS table
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
173
4.4 Exercises in Oracle P4.1. a. Compute the number of neighbors of country ‘Moldavia’.
b. Parameterize this query and compute the numbers of neighbors
of country ‘Serbia’.
Solution:
a.
Firstly, please note that, due to NEIGHBORS antisymmetry, any country
may appear in this table instance either only in the Country column (e.g.
Romania), or only in the Neighbor one (e.g. Hungary), or in both of them
(e.g. Serbia, Bulgaria, Ukraine).
Trivially, the only table we need is NEIGHBORS, but, generally, for any
country, we have to add the number of its occurrences in both columns
(corresponding to this math relation canonical Cartesian projections).
Consequently, the SQL solution (save it as query P4_1) is the following:
SELECT COUNT(*) AS MoldaviaNeighborNo FROM NEIGHBORS
WHERE COUNTRY =
(SELECT x FROM COUNTRIES WHERE Country = 'Moldavia')
OR NEIGHBOR =
(SELECT x FROM COUNTRIES WHERE Country = 'Moldavia');
Please note first that Oracle only evaluates once the common subquery;
the only faster solution is when we know the corresponding country code
(as no access to COUNTRIES is needed anymore):
SELECT COUNT(*) AS MoldaviaNeighborNo FROM NEIGHBORS
WHERE COUNTRY = 2 OR NEIGHBOR = 2;
Note that if irreflexivity is not enforced, then any row of type <x,x>
would vitiate this result (as compared to the mental corresponding com-
putation): for example, if the table instance would also contain line <11,
2, 2>, then (because of the OR), SQL computes 3, whereas there are 4 oc-
currences of 2 (two in Countries and 2 in Neighbors). Obviously, this is
just another reason for enforcing irreflexivity of NEIGHBORS too.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
174
b.
Parameterization of P4_1a is straightforward:
SELECT COUNT(*) AS CountryNeighborNo FROM NEIGHBORS
WHERE COUNTRY =
(SELECT x FROM COUNTRIES WHERE Country =
:Please_enter_country_name)
OR NEIGHBOR =
(SELECT x FROM COUNTRIES WHERE Country =
:Please_enter_country_name);
Figure 4.17 shows the window for passing “Serbia” as an actual parame-
ter value to it, whereas 4.18 presents its result for this value:
Figure 4.17 Passing an actual parameter value to query P4_1b
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
175
Figure 4.18 Query P4_1b result for “Serbia”
As Oracle does not accept parameterized views, the best way to save
P_41b is as a PL/SQL stored function that takes a country name as its pa-
rameter and returns the corresponding number of neighbors. For creating
such a function, right-click on the Function node of the LAB_DB db and
then click on New Function…:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
176
Figure 4.19 Creating a PL/SQL function
In the Create PL/SQL Function window that pops up, enter function
Name, change the “<Return>” parameter type to Number, add a VAR-
CHAR2 IN(PUT) Country_Name parameter, and then click OK:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
177
Figure 4.20 Specifying a PL/SQL function signature
Click on the save icon to save this initially null returning function:
Figure 4.21 Saving a PL/SQL function definition
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
178
Change return type from number to int, define a int cnn variable, save in-
to it the result of the SELECT COUNT(*), and return it (instead of null);
do not forget to replace both occurences of the SQL bind variable
:Please_enter_country_name with the Country_Name parameter,
and, finally, save your modifications:
Figure 4.22 Modifying a PL/SQL function body
The same result may be obtained by running the following DDL SQL
script into a LAB_DB SQL pane:
------------------------------------------------------
-- DDL for Function P4_1B
------------------------------------------------------
CREATE OR REPLACE FUNCTION "LAB_DB"."P4_1B"
(
country_name in varchar2
) return int as
cnn int;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
179
begin
SELECT COUNT(*) AS CountryNeighborNo INTO cnn
FROM NEIGHBORS
WHERE COUNTRY =
(SELECT x FROM COUNTRIES
WHERE Country = country_name)
OR NEIGHBOR =
(SELECT x FROM COUNTRIES
WHERE Country = country_name);
return cnn;
end p4_1b;
/
From now on, you can use this function both programmatically and in
SQL (and, if you wish, may move it into a PL/SQL package); for example,
if you type SELECT P4_1B(‘Serbia’) AS SerbiaNeighborsNo
FROM DUAL;14
in a LAB_DB SQL pane of the SQL Developer and run it,
you get the corresponding result:
Figure 4.23 Using a PL/SQL function
14
DUAL is an underlying table of Oracle’s metadata catalog having only one row.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
180
P4.2 Compute the set of countries without neighbors, in ascending order
of their names.
Solution:
Obviously, all needed data is stored in COUNTRIES and NEIGHBORS
and, again, due to NEIGHBORS math relation antisymmetry, we have to
look up for such countries in both Country and Neighbor columns.
The following SQL solution is the simplest and most elegant one (save it
as view P4-2):
SELECT COUNTRY FROM COUNTRIES WHERE X NOT IN
(SELECT DISTINCT COUNTRY FROM NEIGHBORS
UNION
SELECT DISTINCT NEIGHBOR FROM NEIGHBORS)
ORDER BY COUNTRY;
Figure 4.24 shows its result; note that, just like in math, the SQL UNION
operator eliminates duplicates15
.
Figure 4.24 Query P4_2 result
15
Whenever you need such duplicates, use UNION ALL instead.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
181
Note that, as UNION is the slowest operator, replacing it with an
equivalent AND one yields a faster solution (0.005” instead of 0.007” on
my PC):
SELECT COUNTRY FROM COUNTRIES
WHERE x NOT IN (SELECT DISTINCT COUNTRY FROM NEIGHBORS)
AND x NOT IN (SELECT DISTINCT NEIGHBOR FROM NEIGHBORS)
ORDER BY COUNTRY;
An even faster, subqueryless equivalent solution (0.001” on my PC, save
it as P4_2Even_Faster) makes use of outer joins, the Is Null predicate,
and a queries hierarchy based on two dual preliminary views (P4_2C and
P4-2N):
P4-2C:
SELECT DISTINCT Country AS x FROM NEIGHBORS;
P4-2N:
SELECT DISTINCT Neighbor AS x FROM NEIGHBORS;
P4-2Fastest:
SELECT Country
FROM (COUNTRIES LEFT JOIN P4_2C
ON COUNTRIES.x = P4_2C.x) LEFT JOIN
P4_2N ON COUNTRIES.x = P4_2N.x
WHERE P4_2C.x Is Null AND P4_2N.x Is Null
ORDER BY COUNTRIES.Country;
Note that without DISTINCT, the result is the same, but it takes more
time to be computed: for example, P4-2C above computes the set {1, 2,
25, 26, 29}, whereas SELECT Country AS x FROM NEIGHBORS; is
computing the list {1, 1, 1, 1, 1, 2, 25, 25, 26, 29}.
The fastest possible (0.006” on my PC, save it as view P4_2Fastest), sim-
pler, one step query, which is not using either UNION, or subqueries, or
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
182
queries hierarchies, but is using two instances of the table NEIGHBORS is
the following16
:
SELECT DISTINCT COUNTRIES.Country
FROM (COUNTRIES LEFT JOIN NEIGHBORS
ON COUNTRIES.x = NEIGHBORS.Country) LEFT JOIN
NEIGHBORS NEIGHBOR_COUNTRIES
ON COUNTRIES.x = NEIGHBOR_COUNTRIES.Neighbor
WHERE NEIGHBORS.Country Is Null AND
NEIGHBOR_COUNTRIES.Neighbor Is Null
ORDER BY COUNTRIES.Country;
P4.3. a. Compute the set of pairs <country name, neighbors number> for
countries having at least k neighbors (k natural), in descending order of
the number of their neighbors and then ascending on their names.
b. What are the corresponding results for k = 3 and k = 0?
Solution:
Obviously, the corresponding result for any k 0, manually computed (by
writing all countries from COUNTRIES, in alphabetic order, in the first
column of the table shown in figure 4.25, counting how many times each
one appears in NEIGHBOR.Country and writing the result in the Right-
Neighbors column, then same thing for the NEIGHBOR.Neighbor and
writing the result in the LeftNeighbors one, and then adding the two pre-
vious column values and writing them down in the TotalNeighbors co-
lumn), is the following (but only in the alphabetic order of Country, not in
the one asked by the exercise).
As even manually this problem is sub-dividable in smaller problems, let’s
program it in SQL in the same manner, step by step:
P4_3a0C (compute total number of neighbors per country for Country):
SELECT Country, Count(Neighbor) AS N#
FROM NEIGHBORS GROUP BY Country;
16
Note that Oracle does not accept the AS keyword: the renaming RA operator simply
does not have any name!
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
183
Country RightNeighbors LeftNeighbors TotalNeighbors
Bulgaria 1 2 3
Greece 0 1 1
Hungary 0 3 3
Malta 0 0 0
Moldavia 1 1 2
Romania 5 0 5
Serbia 2 1 3
U.K. 0 0 0
U.S.A. 0 0 0
Ukraine 1 2 3
Figure 4.25 Results of manually computing total number of neighbors per
country (in the ascending order of country names)
P4_3a0N (compute total number of neighbors per country for Neighbor):
SELECT Neighbor, Count(Country) AS N#
FROM NEIGHBORS GROUP BY Neighbor;
P4_3a1B (compute total number of neighbors per country for countries
occurring both in Country and Neighbor):
SELECT Country,
P4_3a0c.N# + P4_3a0n.N# AS NeighborsInBothSides
FROM P4_3a0C INNER JOIN P4_3a0N
ON P4_3a0C.Country = P4_3a0N.Neighbor;
P4_3a1L (compute total number of neighbors per country for countries
occurring only in Country):
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
184
SELECT Country, P4_3a0C.N#
FROM P4_3a0C LEFT JOIN P4_3a0N
ON P4_3a0C.Country = P4_3a0N.Neighbor
WHERE Neighbor Is Null;
P4_3a1R (compute total number of neighbors per country for countries
occurring only in Neighbor):
SELECT Neighbor, P4_3a0N.N#
FROM P4_3a0N LEFT JOIN P4_3a0C
ON P4_3a0N.Neighbor = P4_3a0C.Country
WHERE Country Is Null;
P4_3a2 (union results of P4-2, for countries with no neighbors, slightly
modified in order to select x instead of Country, with the ones of P4_
3a1B, P4_3a1L, and P4_3a1R):
SELECT x, 0 AS NeighborsNo FROM COUNTRIES
WHERE x NOT IN
(SELECT DISTINCT COUNTRY FROM NEIGHBORS)
AND x NOT IN
(SELECT DISTINCT NEIGHBOR FROM NEIGHBORS)
UNION
SELECT * FROM P4_3a1B
UNION
SELECT * FROM P4_3a1L
UNION
SELECT * FROM P4_3a1R;
P4_3a (computing final result; add this procedure too in the LAB_DB_
PL_SQL package):
SELECT Country, NeighborsNo
FROM COUNTRIES INNER JOIN P4_3a2
ON COUNTRIES.x = P4_3a2.x
WHERE NeighborsNo >= :k
ORDER BY NeighborsNo DESC, COUNTRIES.Country;
Figures 4.26 and 4.27 show the corresponding results for k = 3 and k = 0:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
185
Figure 4.26 Result of P4-3a for k = 3
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
186
Figure 4.27 Result of P4-3a for k = 0
The following equivalent solution is embedding all above steps into only
one SELECT statement (using subqueries instead of queries hierarchies):
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
187
SELECT COUNTRIES.COUNTRY, NEIGHBORS#
FROM COUNTRIES INNER JOIN
(SELECT COUNTRY, SUM (N#) NEIGHBORS# FROM
(SELECT COUNTRY, COUNT(*) N#
FROM NEIGHBORS GROUP BY COUNTRY
UNION
SELECT NEIGHBOR, COUNT(*) N#
FROM NEIGHBORS GROUP BY NEIGHBOR
UNION
SELECT x, 0 AS NeighborsNo FROM COUNTRIES
WHERE x NOT IN
(SELECT DISTINCT COUNTRY FROM NEIGHBORS)
AND x NOT IN
(SELECT DISTINCT NEIGHBOR FROM NEIGHBORS))
GROUP BY COUNTRY
HAVING SUM (N#) >= :k) S
ON COUNTRIES.X = S.COUNTRY
ORDER BY NEIGHBORS# DESC, COUNTRIES.COUNTRY;
4.5 Best practice rules
4.6 Homework H4.1 Compute the set of countries that do not appear in column Country
of table NEIGHBORS, in ascending order of their names, without
using sub-queries. What is the corresponding result?
H4.2 Compute the set of pairs <country name, neighbors number>, in
descending order of neighbors number and then ascending on country
name, also including countries that do not have neighbors. What is the
corresponding result?
H4.3 Design, develop, and test a SQL script for adding the fact that
Ukraine is also neighbor to Russia, without knowing anything on the
two table instances except for the fact that Ukraine exists in
COUNTRIES, while Russia does not. What lines will be added
according to the current test instance?
H4.4 Design, develop, and test a SQL script for discarding all
neighborhood data for all countries whose names start with a letter
given as a parameter. What lines will disappear?
H4.5 Prove that left, right, and full outer joins are not only distinct
between them and as compared to the inner join, but also distinct from
the Cartesian product. Hints: give intelligent counter examples; note
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
188
that some wrongly consider the full outer join to be always equal to
the Cartesian product.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
189
Chapter 5. 5th Lab: Self-Joins and Transitive Closures
5.1 Exercises in Access P5.1 Design, develop, and test a SQL script for changing to uppercase the
names of the countries having the property that they are neighbors to
at least one neighbor of one of their neighbors (i.e. country x will have
its name modified if and only if it is neighbor both to countries y and
z, where z is a neighbor of y). Give both subquery and subqueryless
equivalent solutions. What are the countries whose names will be mo-
dified?
Solution:
Inspecting table NEIGHBORS’s instance (see figures 4.2 and 4.4 above),
it is obvious that there are four such countries triangles: Romania, Molda-
via, Ukraine; Romania, Serbia, Bulgaria; Romania, Serbia, Hungary; and
Romania, Hungary, Ukraine. Consequently, all of these six involved
countries will have their names changed to ROMANIA, MOLDOVA,
SERBIA, BULGARIA, HUNGARY, and UKRAINE, respectively.
As a first step, it is a good decision to compute the symmetrical (actual)
math NEIGHBORS relation, in order not to care anymore on which one is
the order in which neighboring data pairs are stored (see figure 5.1 for its
result):
P5-1-a: SELECT COUNTRY, NEIGHBOR FROM NEIGHBORS
UNION
SELECT NEIGHBOR, COUNTRY FROM NEIGHBORS;
In a second step, we should compute the pairs <x, z>, such that there are
pairs <x, y> and <y, z> in P5-1-a (that is neighbors of neighbors), but, tri-
vially, where x z (as even the math NEIGHBORS relation is irreflexive);
to accomplish it, we obviously need two instances of P-5-1-a and a self-
join between them (see figure 5.2 for its result):
P-5-1-b: SELECT DISTINCT [P5-1-a].COUNTRY,
[P5-1-a.TransNeighbors].NEIGHBOR
FROM [P5-1-a] INNER JOIN
[P5-1-a] AS [P5-1-a-TransNeighbors]
ON [P5-1-a].NEIGHBOR = [P5-1-a-TransNeighbors].COUNTRY
WHERE [P5-1-a].COUNTRY <>
[P5-1-a-TransNeighbors].[Neighbor];
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
190
Figure 5.1 Result of P-5-1-a
In a third step, we should filter out from P5-1-b all rows which do not
exist in P5-1-a (that is to enforce x neighbor to z also directly) and only
retain the set of corresponding countries (see figure 5.3 for its result):
P-5-1-c: SELECT DISTINCT [P5-1-a].COUNTRY
FROM [P5-1-a] INNER JOIN [P-5-1-b]
ON [P5-1-a].NEIGHBOR = [P-5-1-b].COUNTRY AND
[P5-1-a].COUNTRY = [P-5-1-b].NEIGHBOR;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
191
Figure 5.2 Result of P-5-1-b
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
192
Figure 5.3 Result of P-5-1-c
Finally, we can update COUNTRIES.Country for all (and only) those
countries whose ids are computed by P-5-1-c:
P-5-1: UPDATE COUNTRIES SET Country = UCase([Country])
WHERE x IN (SELECT * FROM [P-5-1-c]);
By replacing the above queries hierarchy with corresponding subqueries,
the following equivalent statement is obtained:
P-5-1SubQ: UPDATE COUNTRIES SET COUNTRY = UCase(COUNTRY)
WHERE X IN
(SELECT DISTINCT A.COUNTRY FROM
(SELECT COUNTRY, NEIGHBOR FROM NEIGHBORS
UNION
SELECT NEIGHBOR, COUNTRY FROM NEIGHBORS) A
INNER JOIN
(SELECT DISTINCT A.COUNTRY, B.NEIGHBOR FROM
(SELECT COUNTRY, NEIGHBOR FROM NEIGHBORS
UNION
SELECT NEIGHBOR, COUNTRY FROM NEIGHBORS) A
INNER JOIN
(SELECT COUNTRY, NEIGHBOR FROM NEIGHBORS
UNION
SELECT NEIGHBOR, COUNTRY FROM NEIGHBORS) B
ON A.NEIGHBOR = B.COUNTRY AND
A.COUNTRY <> B.NEIGHBOR) B
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
193
ON A.NEIGHBOR = B.COUNTRY AND
A.COUNTRY = B.NEIGHBOR);
Figure 5.4 presents the instance of table COUNTRIES after running P-5-1
or P-5-1SQuery:
Figure 5.4 COUNTRIES’s instance after running P-5-1 or P-5-1SQuery
P5.2 Consider the table from figure 5.5.17
Design, develop, and test Ac-
cess (pure, ANSI 92) SQL queries for computing (both with and without
subqueries) the set of all pairs <z, y> such that y is a descendent of z,
where z and y are corresponding people names, in the ascending order of z
and y (that is the transitive closure of Father Mother). Give a solution
using a queries hierarchy and an equivalent one of only one statement.
What should the result be? Which solution is faster and why?
Solution:
Inspecting table PEOPLE’s instance, it is obvious that it stores the genea-
logical tree (of King Mihai I of Romania) shown in figure 5.6.
Using the Left-Root-Right traversal18
of this tree, the answer (trivially not
in the desired order) of any such query should be the set from figure 5.7.
17
Note that, obviously, as they model (genealogical) trees, both Father and Mother
should always be acyclic (cycle free)! 18
Note that this is an algebraic-type algorithm, dealing with elements of sets (one by
one).
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
194
PEOPLE (x, Name)
x Name Father Mother
Autonumber ASCI(255) Im(x) Im(x)
NOT NULL NOT NULL
1 Queen Victoria of U.K.
2 Duke Alfred of Saxe-Coburg & Gotha 1
3 King Alexander II of Russia
4 Princess Maria Alexandrovna 3
5 Queen Maria of Romania 2 4
6 King Ferdinand I of Romania
7 King Carol II of Romania 6 5
8 King Frederic III of Prussia
9 Queen Sofia of Greece 8
10 King Constantin I of Greece
11 Queen Elena of Romania 10 9
12 King Mihai I of Romania 7 11
Figure 5.5 An instance fragment for the genealogical tree of the latest
Romania’s Kings and Queens
Queen Victoria of U.K. King Alexander II of Russia
Duke Alfred Princess Maria Alexandrovna King Frederic III of Prussia
Queen Maria King Ferdinand I Queen Sofia King Constantin I
King Carol II of Romania Queen Elena of Romania
King Mihai I of Romania
Figure 5.6 The genealogical tree stored in table PEOPLE
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
195
Ancestor Descendent
Queen Victoria of U.K. Duke Alfred of Saxe-Coburg &
Gotha
Queen Victoria of U.K. Queen Maria of Romania
Duke Alfred of Saxe-Coburg &
Gotha
Queen Maria of Romania
Princess Maria Alexandrovna Queen Maria of Romania
King Alexander II of Russia Queen Maria of Romania
King Alexander II of Russia Princess Maria Alexandrovna
Queen Maria of Romania King Carol II of Romania
Queen Maria of Romania King Mihai I of Romania
King Carol II of Romania King Mihai I of Romania
Queen Elena of Romania King Mihai I of Romania
Queen Sofia of Greece Queen Elena of Romania
King Frederic III of Prussia Queen Sofia of Greece
King Frederic III of Prussia Queen Elena of Romania
King Constantin I of Greece Queen Elena of Romania
King Constantin I of Greece King Mihai I of Romania
Figure 5.7 The (transitive closure) set that should be the answer of P5-2
As you were taught in the previous lecture, SQL (just like the first order
logic on which it is based) is meant to exploit a dual, parallel-type ap-
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
196
proach19
, of computing, for example, all descendants of all direct ances-
tors (mothers and fathers) in only one step; consequently, the best first ap-
proach to any problem of this type is to start by computing pairs of <pa-
rents, children> (level one), then to continue with pairs of <grandparents,
grandchildren> (level two), <grand-grandparents, grand-grandchildren>
(level three), etc., and, finally, to merge them in order to compute the re-
quested result. Generally, at least in logic and dbs, DO NOT THINK IN
TERMS OF ELEMENTS OF SETS, BUT RATHER IN TERMS OF
SETS OF ELEMENTS!
Obviously, the following statement computes (see figure 5.8 for the cor-
responding result) the first level of this hierarchy (genealogical tree):
19
Dually, logic type approaches deal with sets of elements (processing all their elements
in parallel, in only one step).
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
197
P5-2-L1:
SELECT Father AS Ancestor, x AS Descendant FROM PEOPLE
WHERE Father Is Not Null
UNION
SELECT Mother AS Ancestor, x AS Descendant FROM PEOPLE
WHERE Mother Is Not Null;
Figure 5.8 The set of pairs <parents, children>
Note that we are not interested in pairs of the type <null, y>!
Then, the following statement computes (see figure 5.9 for the corres-
ponding result) the second level of this hierarchy (genealogical tree):
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
198
P5-2-L2:
SELECT [P5-2-L1].Ancestor, [P5-2-L1_1].Descendant
FROM [P5-2-L1] INNER JOIN [P5-2-L1] AS [P5-2-L1_1]
ON [P5-2-L1].Descendant = [P5-2-L1_1].Ancestor;
Figure 5.9 The set of pairs <grandparents, grandchildren>
Note that, indeed, by using two instances of query P5-2-L1, we are easily
obtaining the fathers/mothers of fathers/mothers – that is the grandparents
of the (grand)children, with a self-join.
Next, the following statement computes (see figure 5.10 for the corres-
ponding result) the third level of this hierarchy (genealogical tree):
P5-2-L3:
SELECT [P5-2-L2].Ancestor, [P5-2-L1].Descendant
FROM [P5-2-L2] INNER JOIN [P5-2-L1]
ON [P5-2-L2].Descendant = [P5-2-L1].Ancestor;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
199
Figure 5.10 The set of pairs <grand-grandparents, grand-grandchildren>
Next, the following statement computes (see figure 5.11 for the corres-
ponding result) the fourth level of this hierarchy (genealogical tree):
P5-2-L4:
SELECT [P5-2-L3].Ancestor, [P5-2-L1].Descendant
FROM [P5-2-L3] INNER JOIN [P5-2-L1]
ON [P5-2-L3].Descendant = [P5-2-L1].Ancestor;
Figure 5.11 The set of pairs <grand-grand-grandparents, grand-grand-
grandchildren>
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
200
Note that you could compute level four alternatively with two instances of
P5-2-L2 instead20
.
Next, the following statement computes (see figure 5.12 for the corres-
ponding result) the fifth level of this hierarchy (genealogical tree):
P5-2-L5:
SELECT [P5-2-L4].Ancestor, [P5-2-L1].Descendant
FROM [P5-2-L4] INNER JOIN [P5-2-L1]
ON [P5-2-L4].Descendant = [P5-2-L1].Ancestor;
Figure 5.12 The set of pairs <grand-grand-grand-grandparents, grand-
grand-grand-grandchildren>
Obviously, as the result of this query is the empty set, this means that
there are no such pairs, so there is no use to continue, as there will not be
any other pairs of higher level either. Note that, indeed, the longest paths
in the tree from figure 5.6 above (those from Queen Victoria of U.K. and
King Alexander II of Russia to King Mihai I of Romania) have length 4,
so this tree height is four.
Consequently, in order to get the almost final result we only need to union
all queries computing the first four levels (see figure 5.13 for the corres-
ponding result):
P5-2-TransClosure:
SELECT * FROM [P5-2-L1]
UNION
20
As, indeed, 3 + 1 = 4 = 2 + 2!
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
201
SELECT * FROM [P5-2-L2]
UNION
SELECT * FROM [P5-2-L3]
UNION
SELECT * FROM [P5-2-L4];
Finally, by joining this result with two instances of PEOPLE (one for an-
cestors and one for descendants) and sorting them in the desired ordered,
we get the requested result (see figure 5.14):
P5-2:
SELECT PEOPLE.Name AS Ancestors,
PEOPLE_1.Name AS Descendants
FROM (PEOPLE INNER JOIN [P5-2-TransClosure]
ON PEOPLE.x = [P5-2-TransClosure].Ancestor)
INNER JOIN PEOPLE AS PEOPLE_1
ON PEOPLE_1.x = [P5-2-TransClosure].Descendant
ORDER BY PEOPLE.Name, PEOPLE_1.Name;
Please note that, not only for SQL programmer beginners, there is a huge
temptation in such cases to offer, at a first glance, just another definition
of the empty set: if you do not use two instances of PEOPLE, but only
one, the following query will always compute the empty set, regardless of
the (valid) instance of PEOPLE (as, from the transitivity of equality, from
PEOPLE.x = [P5-2-TransClosure].Ancestor AND PEOPLE.x =
[P5-2-TransClosure].Descendant, it results that [P5-2-Trans-
Closure].Ancestor = [P5-2-TransClosure].Descendant,
which is never happening for valid instances of PEOPLE, as it is acyclic
both for Father and Mother, so it is irreflexive too – that is nobody can be
his/her own ancestor/descendant!):
SELECT Name AS Ancestors,
Name AS Descendants
FROM PEOPLE INNER JOIN [P5-2-TransClosure]
ON PEOPLE.x = [P5-2-TransClosure].Ancestor AND
PEOPLE.x = [P5-2-TransClosure].Descendant
ORDER BY Name, Name;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
202
Figure 5.13 The transitive closure of Father Mother from figure 5.5
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
203
Figure 5.14 The result of the solution to exercise P5-2
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
204
Obviously, the above queries hierarchy can be equivalently replaced by
the following single SQL statement, which makes (heavy) use of
subqueries:
P5-2-SQuery:
SELECT PEOPLE.Name AS Ancestors,
PEOPLE_1.Name AS Descendants
FROM (PEOPLE INNER JOIN
(SELECT Father AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Mother Is Not Null
UNION
SELECT [P5-2-L1].Ancestor, [P5-2-L1_1].Descendant
FROM
(SELECT Father AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Mother Is Not Null)
AS [P5-2-L1] INNER JOIN
(SELECT Father AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Mother Is Not Null)
AS [P5-2-L1_1]
ON [P5-2-L1].Descendant = [P5-2-L1_1].Ancestor
UNION
SELECT [P5-2-L2].Ancestor, [P5-2-L1].Descendant
FROM
(SELECT [P5-2-L1].Ancestor,
[P5-2-L1_1].Descendant
FROM
(SELECT Father AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Mother Is Not Null)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
205
AS [P5-2-L1] INNER JOIN
(SELECT Father AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Mother Is Not Null)
AS [P5-2-L1_1]
ON [P5-2-L1].Descendant = [P5-2-L1_1].Ancestor)
AS [P5-2-L2] INNER JOIN
(SELECT Father AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Mother Is Not Null)
AS [P5-2-L1]
ON [P5-2-L2].Descendant = [P5-2-L1].Ancestor
UNION
SELECT [P5-2-L3].Ancestor, [P5-2-L1].Descendant
FROM
(SELECT [P5-2-L2].Ancestor, [P5-2-L1].Descendant
FROM
(SELECT [P5-2-L1].Ancestor,
[P5-2-L1_1].Descendant
FROM
(SELECT Father AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Mother Is Not Null)
AS [P5-2-L1] INNER JOIN
(SELECT Father AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Mother Is Not Null)
AS [P5-2-L1_1]
ON [P5-2-L1].Descendant = [P5-2-L1_1].Ancestor)
AS [P5-2-L2] INNER JOIN
(SELECT Father AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Father Is Not Null
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
206
UNION
SELECT Mother AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Mother Is Not Null)
AS [P5-2-L1]
ON [P5-2-L2].Descendant = [P5-2-L1].Ancestor)
AS [P5-2-L3] INNER JOIN
(SELECT Father AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother AS Ancestor, x AS Descendant
FROM PEOPLE WHERE Mother Is Not Null)
AS [P5-2-L1]
ON [P5-2-L3].Descendant = [P5-2-L1].Ancestor)
AS [P5-2-TransClosure]
ON PEOPLE.x = [P5-2-TransClosure].Ancestor)
INNER JOIN PEOPLE AS PEOPLE_1
ON PEOPLE_1.x = [P5-2-TransClosure].Descendant
ORDER BY PEOPLE.Name, PEOPLE_1.Name;
Generally, P5-2 is faster than P5-2SQuery (for large enough instances of
PEOPLE), as subqueries are evaluated slower than queries hierarchies.
5.2 Exercises in Oracle P5.1 Design, develop, and test a SQL script for changing to uppercase the
names of the countries having the property that they are neighbors to
at least one neighbor of one of their neighbors (i.e. country x will have
its name modified if and only if it is neighbor both to countries y and
z, where z is a neighbor of y). Give both subquery and subqueryless
equivalent solutions. What are the countries whose names will be mo-
dified?
Solution:
Inspecting table NEIGHBORS’s instance (see figures 4.14 and 4.16
above), it is obvious that there are four such countries triangles: Romania,
Moldavia, Ukraine; Romania, Serbia, Bulgaria; Romania, Serbia, Hun-
gary; and Romania, Hungary, Ukraine. Consequently, all of these six in-
volved countries will have their names changed to ROMANIA, MOLDA-
VIA, SERBIA, BULGARIA, HUNGARY, and UKRAINE, respectively.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
207
As a first step, it is a good decision to compute the symmetrical (actual)
math NEIGHBORS relation, in order not to care anymore on which one is
the order in which neighboring data pairs are stored (see figure 5.15 for
its result):
P51_a: SELECT COUNTRY, NEIGHBOR FROM NEIGHBORS
UNION
SELECT NEIGHBOR, COUNTRY FROM NEIGHBORS;
Figure 5.15 Result of P5_1a
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
208
In a second step, we should compute the pairs <x, z>, such that there are
pairs <x, y> and <y, z> in P5_1a (that is neighbors of neighbors), but, tri-
vially, where x z (as even the math NEIGHBORS relation is irreflexive);
to accomplish it, we obviously need two instances of P5-1a and a self-
join between them (see figure 5.16 for its result):
P5_1b: SELECT DISTINCT P5_1a.COUNTRY,
P5_1a_TransNeighbors.NEIGHBOR
FROM P5_1a INNER JOIN
P5_1a P5_1a_TransNeighbors
ON P5_1a.NEIGHBOR = P5_1a_TransNeighbors.COUNTRY
WHERE P5_1a.COUNTRY <>
P5_1a_TransNeighbors.Neighbor;
In a third step, we should filter out from P5_1b all rows which do not
exist in P5_1a (that is to enforce x neighbor to z also directly) and only
retain the set of corresponding countries (see figure 5.17 for its result):
P5_1c: SELECT DISTINCT P5_1a.COUNTRY
FROM P5_1a INNER JOIN P5_1b
ON P5_1a.NEIGHBOR = P5_1b.COUNTRY AND
P5_1a.COUNTRY = P5_1b.NEIGHBOR;
Finally, we can update COUNTRIES.Country for all (and only) those
countries whose ids are computed by P5_1c:
P5_1: UPDATE COUNTRIES SET Country = Upper(Country)
WHERE x IN (SELECT * FROM P5_1c);
By replacing the above queries hierarchy with corresponding subqueries,
the following equivalent statement is obtained:
P5_1SubQ: UPDATE COUNTRIES SET COUNTRY = Upper(COUNTRY)
WHERE X IN
(SELECT DISTINCT A.COUNTRY FROM
(SELECT COUNTRY, NEIGHBOR FROM NEIGHBORS
UNION
SELECT NEIGHBOR, COUNTRY FROM NEIGHBORS) A
INNER JOIN
(SELECT DISTINCT A.COUNTRY, B.NEIGHBOR FROM
(SELECT COUNTRY, NEIGHBOR FROM NEIGHBORS
UNION
SELECT NEIGHBOR, COUNTRY FROM NEIGHBORS) A
INNER JOIN
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
209
Figure 5.16 Result of P5_1b
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
210
Figure 5.17 Result of P5_1c
(SELECT COUNTRY, NEIGHBOR FROM NEIGHBORS
UNION
SELECT NEIGHBOR, COUNTRY FROM NEIGHBORS) B
ON A.NEIGHBOR = B.COUNTRY AND
A.COUNTRY <> B.NEIGHBOR) B
ON A.NEIGHBOR = B.COUNTRY AND
A.COUNTRY = B.NEIGHBOR);
Figure 5.18 presents the instance of table COUNTRIES after running
P5_1 or P5_1SQuery (save them as procedures, because Oracle views
cannot contain INSERT, UPDATE, or DELETE):
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
211
Figure 5.18 COUNTRIES’s instance after running P5_1 or P5_1SQuery
P5.2 Consider the table from figure 5.19.21
Design, develop, and test pure,
ANSI 92 Oracle SQL queries for computing (both with and without sub-
queries) the set of all pairs <z, y> such that y is a descendent of z, where z
and y are corresponding people names, in the ascending order of z and y
(that is the transitive closure of Father Mother). Give a solution using a
queries hierarchy and an equivalent one of only one statement. What
should the result be? Which solution is faster and why?
Solution:
Inspecting table PEOPLE’s instance, it is obvious that it stores the
genealogical tree (of King Mihai I of Romania) shown in figure 5.20.
21
Note that, obviously, as they model (genealogical) trees, both Father and Mother
should always be acyclic (cycle free)!
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
212
PEOPLE (x, Name)
x Name Father Mother
Autonumber ASCI(255) Im(x) Im(x)
NOT NULL NOT NULL
1 Queen Victoria of U.K.
2 Duke Alfred of Saxe-Coburg & Gotha 1
3 King Alexander II of Russia
4 Princess Maria Alexandrovna 3
5 Queen Maria of Romania 2 4
6 King Ferdinand I of Romania
7 King Carol II of Romania 6 5
8 King Frederic III of Prussia
9 Queen Sofia of Greece 8
10 King Constantin I of Greece
11 Queen Elena of Romania 10 9
12 King Mihai I of Romania 7 11
Figure 5.19 An instance fragment for the genealogical tree of the latest
Romania’s Kings and Queens
Queen Victoria of U.K. King Alexander II of Russia
Duke Alfred Princess Maria Alexandrovna King Frederic III of Prussia
Queen Maria King Ferdinand I Queen Sofia King Constantin I
King Carol II of Romania Queen Elena of Romania
King Mihai I of Romania
Figure 5.20 The genealogical tree stored in table PEOPLE
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
213
Using the Left-Root-Right traversal22
of this tree, the answer (trivially not
in the desired order) of any such query should be the following set:
Ancestor Descendent
Queen Victoria of U.K. Duke Alfred of Saxe-Coburg &
Gotha
Queen Victoria of U.K. Queen Maria of Romania
Duke Alfred of Saxe-Coburg &
Gotha
Queen Maria of Romania
Princess Maria Alexandrovna Queen Maria of Romania
King Alexander II of Russia Queen Maria of Romania
King Alexander II of Russia Princess Maria Alexandrovna
Queen Maria of Romania King Carol II of Romania
Queen Maria of Romania King Mihai I of Romania
King Carol II of Romania King Mihai I of Romania
Queen Elena of Romania King Mihai I of Romania
Queen Sofia of Greece Queen Elena of Romania
King Frederic III of Prussia Queen Sofia of Greece
King Frederic III of Prussia Queen Elena of Romania
King Constantin I of Greece Queen Elena of Romania
King Constantin I of Greece King Mihai I of Romania
Figure 5.21 The (transitive closure) set that should be the answer of P5.2
22
Note that this is an algebraic-type algorithm, dealing with elements of sets (one by
one).
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
214
As you were taught in the previous lecture, SQL (just like the first order
logic on which it is based) is meant to exploit a dual, parallel-type ap-
proach23
, of computing, for example, all descendants of all direct ances-
tors (mothers and fathers) in only one step; consequently, the best first ap-
proach to any problem of this type is to start by computing pairs of <pa-
rents, children> (level one), then to continue with pairs of <grandparents,
grandchildren> (level two), <grand-grandparents, grand-grandchildren>
(level three), etc., and, finally, to merge them in order to compute the re-
quested result. Generally, at least in logic and dbs, DO NOT THINK IN
TERMS OF ELEMENTS OF SETS, BUT RATHER IN TERMS OF
SETS OF ELEMENTS!
Obviously, the following statement computes (see figure 5.22 for the cor-
responding result) the first level of this hierarchy (genealogical tree):
P5_2_L1:
SELECT Father AS Ancestor, x AS Descendant FROM PEOPLE
WHERE Father Is Not Null
UNION
SELECT Mother AS Ancestor, x AS Descendant FROM PEOPLE
WHERE Mother Is Not Null;
23
Dually, logic type approaches deal with sets of elements (processing all their elements
in parallel, in only one step).
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
215
Figure 5.22 The set of pairs <parents, children>
Note that we are not interested in pairs of the type <null, y>!
Then, the following statement computes (see figure 5.23 for the corres-
ponding result) the second level of this hierarchy (genealogical tree):
P5_2_L2:
SELECT P5_2_L1.Ancestor, P5_2_L1_1.Descendant
FROM P5_2_L1 INNER JOIN P5_2_L1 P5_2_L1_1
ON P5_2_L1.Descendant = P5_2_L1_1.Ancestor;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
216
Figure 5.23 The set of pairs <grandparents, grandchildren>
Note that, indeed, by using two instances of query P5_2_L1, we are easily
obtaining the fathers/mothers of fathers/mothers – that is the grandparents
of the (grand)children, with a self-join.
Next, the following statement computes (see figure 5.24 for the corres-
ponding result) the third level of this hierarchy (genealogical tree):
P5_2_L3:
SELECT P5_2_L2.Ancestor, P5_2_L1.Descendant
FROM P5_2_L2 INNER JOIN P5_2_L1
ON P5_2_L2.Descendant = P5_2_L1.Ancestor;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
217
Figure 5.24 The set of pairs <grand-grandparents, grand-grandchildren>
Next, the following statement computes (see figure 5.25 for the corres-
ponding result) the fourth level of this hierarchy (genealogical tree):
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
218
P5_2_L4:
SELECT P5_2_L3.Ancestor, P5_2_L1.Descendant
FROM P5_2_L3 INNER JOIN P5_2_L1
ON P5_2_L3.Descendant = P5_2_L1.Ancestor;
Figure 5.25 The set of pairs <grand-grand-grandparents, grand-grand-
grandchildren>
Note that you could compute level four alternatively with two instances of
P5_2_L2 instead24
.
Next, the following statement computes (see figure 5.26 for the corres-
ponding result) the fifth level of this hierarchy (genealogical tree):
P5_2_L5:
SELECT P5_2_L4.Ancestor, P5_2_L1.Descendant
FROM P5_2_L4 INNER JOIN P5_2_L1
24
As, indeed, 3 + 1 = 4 = 2 + 2!
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
219
ON P5_2_L4.Descendant = P5_2_L1.Ancestor;
Figure 5.26 The set of pairs <grand-grand-grand-grandparents, grand-
grand-grand-grandchildren>
Obviously, as the result of this query is the empty set, this means that
there are no such pairs, so there is no use to continue, as there will not be
any other pairs of higher level either. Note that, indeed, the longest paths
in the tree from figure 5.20 above (those from Queen Victoria of U.K. and
King Alexander II of Russia to King Mihai I of Romania) have length 4,
so this tree height is four.
Consequently, in order to get the almost final result we only need to union
all queries computing the first four levels (see figure 5.27 for the corres-
ponding result):
P5_2_TransClosure:
SELECT * FROM P5_2_L1
UNION
SELECT * FROM P5_2_L2
UNION
SELECT * FROM P5_2_L3
UNION
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
220
SELECT * FROM P5_2_L4;
Figure 5.27 The transitive closure of Father Mother from figure 5.19
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
221
Finally, by joining this result with two instances of PEOPLE (one for an-
cestors and one for descendants) and sorting them in the desired ordered,
we get the requested result (see figure 5.28):
P5_2:
SELECT PEOPLE.Name AS Ancestors,
PEOPLE_1.Name AS Descendants
FROM (PEOPLE INNER JOIN P5_2_TransClosure
ON PEOPLE.x = P5_2_TransClosure.Ancestor) INNER
JOIN PEOPLE PEOPLE_1
ON PEOPLE_1.x = P5_2_TransClosure.Descendant
ORDER BY PEOPLE.Name, PEOPLE_1.Name;
Please note that, not only for SQL programmer beginners, there is a huge
temptation in such cases to offer, at a first glance, just another definition
of the empty set: if you do not use two instances of PEOPLE, but only
one, the following query will always compute the empty set, regardless of
the (valid) instance of PEOPLE (as, from the transitivity of equality, from
PEOPLE.x = P5_2_TransClosure.Ancestor AND PEOPLE.x =
P5_2_TransClosure.Descendant, it results that P5_2_TransClo-
sure.Ancestor = P5_2_TransClosure.Descendant, which is
never happening for valid instances of PEOPLE, as it is acyclic both for
Father and Mother, so it is irreflexive too – that is nobody can be his/her
own ancestor/descendant!):
SELECT Name Ancestors,
Name Descendants
FROM PEOPLE INNER JOIN P5_2_TransClosure
ON PEOPLE.x = P5_2_TransClosure.Ancestor AND
PEOPLE.x = P5_2_TransClosure.Descendant
ORDER BY Name, Name;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
222
Figure 5.28 The result of the solution to exercise P5.2
Obviously, the above queries hierarchy can be equivalently replaced by
the following single SQL statement, which makes (heavy) use of
subqueries:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
223
P5_2_SQuery:
SELECT PEOPLE.Name Ancestors,
PEOPLE_1.Name Descendants
FROM (PEOPLE INNER JOIN
(SELECT Father Ancestor, x Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother, x
FROM PEOPLE WHERE Mother Is Not Null
UNION
SELECT P5_2_L1.Ancestor, P5_2_L1_1.Descendant
FROM
(SELECT Father Ancestor, x Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother, x
FROM PEOPLE WHERE Mother Is Not Null) P5_2_L1
INNER JOIN
(SELECT Father Ancestor, x Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother, x
FROM PEOPLE WHERE Mother Is Not Null)
P5_2_L1_1
ON P5_2_L1.Descendant = P5_2_L1_1.Ancestor
UNION
SELECT P5_2_L2.Ancestor, P5_2_L1.Descendant
FROM
(SELECT P5_2_L1.Ancestor,
P5_2_L1_1.Descendant
FROM
(SELECT Father Ancestor, x Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother, x
FROM PEOPLE WHERE Mother Is Not Null)
P5_2_L1 INNER JOIN
(SELECT Father Ancestor, x Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
224
SELECT Mother, x
FROM PEOPLE WHERE Mother Is Not Null) P5_2_L1_1
ON P5_2_L1.Descendant = P5_2_L1_1.Ancestor)
P5_2_L2 INNER JOIN
(SELECT Father Ancestor, x Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother, x
FROM PEOPLE WHERE Mother Is Not Null) P5_2_L1
ON P5_2_L2.Descendant = P5_2_L1.Ancestor
UNION
SELECT P5_2_L3.Ancestor, P5_2_L1.Descendant
FROM
(SELECT P5_2_L2.Ancestor, P5_2_L1.Descendant
FROM
(SELECT P5_2_L1.Ancestor,
P5_2_L1_1.Descendant
FROM
(SELECT Father Ancestor, x Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother, x
FROM PEOPLE WHERE Mother Is Not Null)
P5_2_L1 INNER JOIN
(SELECT Father Ancestor, x Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother, x
FROM PEOPLE WHERE Mother Is Not Null)
P5_2_L1_1
ON P5_2_L1.Descendant = P5_2_L1_1.Ancestor)
P5_2_L2 INNER JOIN
(SELECT Father Ancestor, x Descendant
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother, x
FROM PEOPLE WHERE Mother Is Not Null) P5_2_L1
ON P5_2_L2.Descendant = P5_2_L1.Ancestor)
P5_2_L3 INNER JOIN
(SELECT Father Ancestor, x Descendant
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
225
FROM PEOPLE WHERE Father Is Not Null
UNION
SELECT Mother, x
FROM PEOPLE WHERE Mother Is Not Null) P5_2_L1
ON P5_2_L3.Descendant = P5_2_L1.Ancestor)
P5_2_TransClosure
ON PEOPLE.x = P5_2_TransClosure.Ancestor)
INNER JOIN PEOPLE PEOPLE_1
ON PEOPLE_1.x = P5_2_TransClosure.Descendant
ORDER BY PEOPLE.Name, PEOPLE_1.Name;
Generally, P5_2 is faster than P5_2SQuery (for large enough instances of
PEOPLE), as subqueries are evaluated slower than queries hierarchies:
for example, on my PC, the first one takes 0.06”, while the second 0.085”.
5.3 Best practice rules
5.4 Homework H5.0 Create table PEOPLE in your lab db and fill it with figure 5.2
instance data.
H5.1 Compute in SQL, for the instance of table PEOPLE (see figure 5.2
above) the transitive closure of Father, union it with that of Mother, and
note that the result is included in the result of P5-2, but the reverse is not
true. Generalize your findings and use them to prove that, for any two
auto-functions f and g defined on and taking values from a same set, f+
g+ (f
g)
+, but the converse is not true.
H5.2 Compute in SQL all files on your PC under the Windows folder for
the first four tree depth levels.
H5-3 Prove by induction that, in order to computer in SQL all files of le-
vel n, n natural, belonging to a subtree of files arbitrarily chosen, you ne-
ed n-1 unions and, for each member i (1 i n) of these unions, you need
either i-1 subqueries or i instances of table FILES and i-1 joins between
them.
H5-4 Prove that pure SQL cannot compute the above query when n is a
parameter. Hint: There is no such thing as “…”between either joins or
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
226
unions; you cannot write, for example, INNER JOIN ... INNER
JOIN and/or UNION .... UNION.
H5-5 As a corollary, prove that pure SQL cannot compute transitive clo-
sures either.
(Recall from sets algebra that, given any set and any binary, transitive
operator on it, the transitive closure of that set is the smallest superset
that includes all transitively computed elements of the set. For example,
given the following FILES instance:
FILES.x FILES.Folder
3 1
4 3
5 4
The corresponding Folder+ transitive closure is {<3,1>, <4,3>, <5,4>,
<4,1>, <5,3>, <5,1>}.)
H5-6 Write a program in any high level programming language of your
choice, which takes as input a table name, a column name which is an
autoforeign key in that table, an arbitrary value of that column, and an
arbitrary value of tree depth and outputs the corresponding SQL
statements for computing the transitive subclosure restricted to level n of
that table instance for that column name and initial value. Do it with
either subqueries, or self-joins, or query hierarchies, at your choice.
H5-7 Generalize 5-6 above by adding a fifth parameter, e.g. of type inte-
ger, but only accepting, e.g., -1 for subqueries, 0 for query hierarchies,
and 1 for self joins. Run this program for table “FILES”, column “Fol-
der”, and your PC's C drive folders, by choosing a subtree having at least
10 levels.
H5-8 Write a high level programming language method for computing all
the first four level of depth in table FILES without SQL.
H5-9 Write a program in a high level programming language embedding
SQL (e.g. Java, C#, VBA, PHP, etc.) that implements the following
algorithm for computing the transitive closure, where FolderValue is a
parameter, and run it for your PC O.S. data:
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
227
TransClosureAlgorithm:
INT i, card, oldcard;
Exec "DELETE FROM Tmp"; //in VBA: DoCmd.RunSQL= Exec
i = 0;
oldcard = 0;
Exec "INSERT INTO Tmp(File, Folder, Level) SELECT
x, Folder, 0 FROM FILES WHERE Folder=" & FolderValue;
card = DCount("*","Tmp"); // in Access, DCount returns the
// cardinal of the corresponding set (here, table Tmp instance)
while oldcard card
oldcard = card;
Exec "INSERT INTO Tmp(File, Folder, Level)
SELECT x, Folder, " & i+1 & " FROM FILES INNER JOIN
Tmp ON FILES.Folder=Tmp.File WHERE Level=" & i & "
AND Folder =" & FolderValue;
i = i + 1;
card = DCount("*", "Tmp");
endwhile;
NOTE: DO NOT FORGET THAT NEXT WEEK YOU SHOULD
PASS YOUR FIRST (SQL) DB TEST (WHICH IS WORTH 10%,
THAT IS 1p FROM YOUR FINAL MARK); GOOD LUCK!
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
228
Chapter 6. 6th Lab: First test (SQL) 90 minutes, 27 points, open book, individual, no electronic devices
6.1 Number 1 a. (2p.) Design and develop a SQL script for adding to the lab db the city
“Sinaia” from the Romania’s state “Prahova”, knowing that Romania’s id
is 1 and that state “Prahova” does not exist either.
b. (3.5p.) Design and develop a SQL script for adding to the lab db the
function BirthPlace : PEOPLE CITIES, having the following graph
and knowing that there is only one city named “Sinaia”:
x City BirthPlace(x)
7 Sinaia
12 Sinaia
c. (Only in Access)(4.5p.) Describe needed steps for adding a lookup
combo-box to BirthPlace, without using the Lookup Wizard.
c. (Only in Oracle) (3.5p.) Design and develop a SQL statement for
creating a view CITIES_STATES_CONTRIES, which computes the
following result:
x City, State, Country
7 Bucharest, Bucharest, ROMANIA
23 Chișinău, Chișinău, MOLDAVIA
41 Constanța, Constanța, ROMANIA
43 Iași, Iași, ROMANIA
25 London, Greater London, U.K.
44 Memphis, Tennessee, U.S.A.
42 New York, New York, U.S.A.
45 Sibiu, Sibiu, ROMANIA
24 Washington, District of Columbia, U.S.A.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
229
d. (17p Access/18p Oracle) Compute, without using subqueries, the set of
triples <Name, ChildrenNo, GrandChildrenNo>, in the descending order
of ChildrenNo and GrandChildrenNo, and then ascending on Name, for
all grandfathers that have at least k children, each of which having at least
n children (k and n being natural parameters). Which is the result for the
PEOPLE lab db instance when k = n = 1 and k = n = 2?
Hints offered as answers to students’ questions during tests:
a. No, there is not only one form of the INSERT SQL statement!
b. Yes, the Key Propagation Principle should be applied in all such
cases!
c. The corresponding SELECT statement is worth 3.5p and the most
important combo-boxes’ property 0.5p.
d. No, there are no ChildrenNo and GrandChildrenNo fields in the
PEOPLE table, but they are computable!
6.1.1 Access solution
a.
INSERT INTO STATES (State, Country)
VALUES (“Prahova”, 1); (0.5p)
INSERT INTO CITIES (City, State)
SELECT “Sinaia”, x FROM STATES
WHERE State = “Prahova” AND Country = 1; (1.5p)
b.
ALTER TABLE PEOPLE ADD COLUMN BirthPlace LONG;
(0.5p)
ALTER TABLE PEOPLE ADD CONSTRAINT fkBPlace
FOREIGN KEY (BirthPlace) REFERENCES CITIES; (1.25p)
UPDATE PEOPLE SET BirthPlace = DLookup(“x”,
“CITIES”, “City=’Sinaia’) WHERE x IN (7, 12);
(1.75p)
c.
- open PEOPLE in Design mode; (0.05p)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
230
- on the BirthPlace line, in the Lookup tab, switch the Display Control
property from Text to Combo box; (0.05p)
- in the Row Source property, type:
SELECT CITIES.x, [City] & ", " & [STATES].[State]
& ", " & [COUNTRIES].[Country]
AS [City, State, Country]
FROM COUNTRIES INNER JOIN (STATES INNER JOIN
CITIES ON STATES.x = CITIES.State)
ON COUNTRIES.x = STATES.Country
ORDER BY [City] & ", " & [STATES].[State] & ", "
& [COUNTRIES].[Country]; (3.5p)
- set the Column Count property to 2; (0.15p)
- set the Column Heads property to Yes; (0.05p)
- set the Column Width property to 0”;4”; (0.05p)
- set the List Rows property to 32; (0.05p)
- set the List Width property to 4”; (0.05p)
- set the Limit to List property to Yes; (0.5p)
- save and close the table scheme window. (0.05p)
d.
- step 1: fathers with at least k children
PeopleHavingAtLeastkChildren: (5p)
SELECT PEOPLE.x, Count(CHILDREN.x) AS ChildrenNo
FROM PEOPLE AS CHILDREN INNER JOIN PEOPLE
ON CHILDREN.Father = PEOPLE.x
GROUP BY PEOPLE.x
HAVING Count(CHILDREN.x) >=
[Enter desired minimum number of children:];
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
231
- step 2: grandfathers with at least k children, each of which has n children
PeopleHavingAtLeastkChildrenAndnGrandChildren: (7p)
SELECT PeopleHavingAtLeastkChildren.x,
PeopleHavingAtLeastkChildren.ChildrenNo,
Count(GrandChildren.x) AS GrandChildrenNo
FROM PEOPLE AS GrandChildren INNER JOIN (PEOPLE AS CHILDREN
INNER JOIN PeopleHavingAtLeastkChildren
ON CHILDREN.Father = PeopleHavingAtLeastkChildren.x)
ON GrandChildren.Father = CHILDREN.x
GROUP BY PeopleHavingAtLeastkChildren.x,
PeopleHavingAtLeastkChildren.ChildrenNo
HAVING Count(GrandChildren.x) >=
[Enter desired minimum number of grand children:];
- step 3: getting names and sorting
Test-1d: (3p)
SELECT PEOPLE.Name,
PeopleHavingAtLeastkChildrenAndnGrandChildren.ChildrenNo,
PeopleHavingAtLeastkChildrenAndnGrandChildren.GrandChildrenNo
FROM PEOPLE INNER JOIN
PeopleHavingAtLeastkChildrenAndnGrandChildren
ON PEOPLE.x = PeopleHavingAtLeastkChildrenAndnGrandChildren.x
ORDER BY
PeopleHavingAtLeastkChildrenAndnGrandChildren.ChildrenNo DESC,
PeopleHavingAtLeastkChildrenAndnGrandChildren.GrandChildrenNo
DESC, PEOPLE.Name;
Figure 6.1 Result of query Test-1d for k = n = 1 (1p)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
232
Figure 6.2 Result (empty set) of query Test-1d for k = n = 2 (1p)
6.1.2 Oracle solution
a.
INSERT INTO STATES (State, Country)
VALUES ('Prahova', 1); (0.5p)
INSERT INTO CITIES (City, State)
SELECT 'Sinaia', x FROM STATES
WHERE State = 'Prahova' AND Country = 1; (1.5p)
b.
ALTER TABLE PEOPLE ADD BirthPlace NUMBER(6,0);
(0.5p)
ALTER TABLE PEOPLE ADD CONSTRAINT fkBPlace
FOREIGN KEY (BirthPlace) REFERENCES CITIES;
(1.25p)
UPDATE PEOPLE SET BirthPlace = (SELECT x FROM
CITIES WHERE City='Sinaia') WHERE x IN (7, 12);
(1.75p)
c.
-------------------------------------------------
-- DDL for View CITIES_STATES_CONTRIES
-------------------------------------------------
CREATE OR REPLACE FORCE VIEW
"LAB_DB"."CITIES_STATES_CONTRIES"
("X", "City, State, Country") AS
SELECT CITIES.x, City || ', ' || STATES.State ||
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
233
', ' || COUNTRIES.Country "City, State, Country"
FROM COUNTRIES INNER JOIN (STATES INNER JOIN
CITIES ON STATES.x = CITIES.State)
ON COUNTRIES.x = STATES.Country
ORDER BY City || ', ' || STATES.State || ', ' ||
COUNTRIES.Country; (3.5p)
d.
- step 0: create a temporary table for storing fathers with at least k
children (1p)
-------------------------------------------------
-- DDL for Table TMPPHLKC
-------------------------------------------------
CREATE TABLE "LAB_DB"."TMPPHLKC"
( "X" NUMBER(6,0),
"CHILDRENNO" NUMBER(2,0)
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1
MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT
CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS";
- step 1: add to the LAB_DB_PL_SQL package the following procedure:
procedure AtLeastkChildrennGrandChildren
(Min_no_of_children in integer,
Min_no_of_grand_children in integer,
rc out GenericCursorType);
procedure AtLeastkChildrennGrandChildren
(Min_no_of_children in integer,
Min_no_of_grand_children in integer,
rc out GenericCursorType) is
begin
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
234
--0. initialize tmpPHLkC (0.5p) DELETE FROM tmpPHLkC;
--1. compute and store into it fathers with at least k
--children (4.5p) INSERT INTO tmpPHLkC
SELECT PEOPLE.x, Count(CHILDREN.x) ChildrenNo
FROM PEOPLE CHILDREN INNER JOIN PEOPLE
ON CHILDREN.Father = PEOPLE.x
GROUP BY PEOPLE.x
HAVING Count(CHILDREN.x) >= Min_no_of_children;
--2. compute and return grandfathers with at least k
--children, each of which has n children (9p) OPEN rc FOR
SELECT NAMES.Name, ChildrenNo,
Count(GrandChildren.x) GrandChildrenNo
FROM (PEOPLE GrandChildren INNER JOIN (PEOPLE CHILDREN
INNER JOIN tmpPHLkC
ON CHILDREN.Father = tmpPHLkC.x)
ON GrandChildren.Father = CHILDREN.x) INNER JOIN PEOPLE
NAMES ON tmpPHLkC.x = NAMES.x
GROUP BY NAMES.Name, tmpPHLkC.ChildrenNo
HAVING Count(GrandChildren.x) >= Min_no_of_grand_children
ORDER BY ChildrenNo DESC, Count(GrandChildren.x) DESC,
NAMES.Name;
end AtLeastkChildrennGrandChildren;
- step 3: test for k = n = 1, respectively 2
var c refcursor;
exec LAB_DB_PL_SQL.AtLeastkChildrennGrandChildren(1,1,:c);
print c;
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
235
Figure 6.3 Result of stored procedure
AtLeastkChildrennGrandChildren for k = n = 1 (1p)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
236
exec LAB_DB_PL_SQL.AtLeastkChildrennGrandChildren(2,2,:c);
print c;
Figure 6.4 Result (empty set) of stored procedure
AtLeastkChildrennGrandChildren for k = n = 2 (1p)
6.2 Number 2 a. (2p.) Design and develop a SQL script for adding to the lab db the city
“Curtea de Argeș” from the Romania’s state “Argeș”, knowing that Ro-
mania’s id is 1 and that state “Argeș” does not exist either.
b. (3.5p.) Design and develop a SQL script for adding to the lab db the
function BurrialPlace: PEOPLE CITIES, having the following graph
and knowing that there is only one city named “Curtea de Argeș”:
x City BurrialPlace (x)
7 Curtea de Argeș
6 Curtea de Argeș
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
237
c. (Only in Access)(4.5p.) Describe needed steps for adding a lookup
combo-box to BurrialPlace, without using the Lookup Wizard.
c. (Only in Oracle) (3.5p.) Design and develop a SQL statement for
creating a view CITIES_STATES_CONTRIES, which computes the fol-
lowing result:
x City, State, Country
7 Bucharest, Bucharest, ROMANIA
23 Chișinău, Chișinău, MOLDAVIA
41 Constanța, Constanța, ROMANIA
43 Iași, Iași, ROMANIA
25 London, Greater London, U.K.
44 Memphis, Tennessee, U.S.A.
42 New York, New York, U.S.A.
45 Sibiu, Sibiu, ROMANIA
24 Washington, District of Columbia, U.S.A.
d. (17p Access/18p Oracle) Compute, without using subqueries, the set of
triples <Name, ChildrenNo, GrandChildrenNo>, in the descending order
of ChildrenNo and GrandChildrenNo, and then ascending on Name, for
all grandmothers that have at least k children, each of which having at
least n children (k and n being natural parameters). Which is the result for
the PEOPLE lab db instance when k = n = 1 and k = n = 2?
Hints offered as answers to students’ questions during tests:
a. No, there is not only one form of the INSERT SQL statement!
b. Yes, the Key Propagation Principle should be applied in all such
cases!
c. The corresponding SELECT statement is worth 3.5p and the most
important combo-boxes’ property 0.5p.
d. No, there are no ChildrenNo and GrandChildrenNo fields in the
PEOPLE table, but they are computable!
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
238
6.2.1 Access solution
a.
INSERT INTO STATES (State, Country)
VALUES (“Argeș”, 1); (0.5p)
INSERT INTO CITIES (City, State)
SELECT “Curtea de Argeș”, x FROM STATES
WHERE State = “Argeș” AND Country = 1; (1.5p)
b.
ALTER TABLE PEOPLE ADD COLUMN BurrialPlace LONG;
(0.5p)
ALTER TABLE PEOPLE ADD CONSTRAINT fkBurPlace
FOREIGN KEY (BurrialPlace) REFERENCES CITIES;
(1.25p)
UPDATE PEOPLE SET BurrialPlace = DLookup(“x”,
“CITIES”, “City=’Curtea de Argeș’”) WHERE x IN
(7,6); (1.75p)
c.
- open PEOPLE in Design mode; (0.05p)
- on the BurrialPlace line, in the Lookup tab, switch the Display Control
property from Text to Combo box; (0.05p)
- in the Row Source property, type:
SELECT CITIES.x, [City] & ", " & [STATES].[State]
& ", " & [COUNTRIES].[Country]
AS [City, State, Country]
FROM COUNTRIES INNER JOIN (STATES INNER JOIN
CITIES ON STATES.x = CITIES.State)
ON COUNTRIES.x = STATES.Country
ORDER BY [City] & ", " & [STATES].[State] & ", "
& [COUNTRIES].[Country]; (3.5p)
- set the Column Count property to 2; (0.15p)
- set the Column Heads property to Yes; (0.05p)
- set the Column Width property to 0”;4”; (0.05p)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
239
- set the List Rows property to 32; (0.05p)
- set the List Width property to 4”; (0.05p)
- set the Limit to List property to Yes; (0.5p)
- save and close the table scheme window. (0.05p)
d.
- step 1: mothers with at least k children
PeopleHavingAtLeastkChildrenM: (5p)
SELECT PEOPLE.x, Count(CHILDREN.x) AS ChildrenNo
FROM PEOPLE AS CHILDREN INNER JOIN PEOPLE
ON CHILDREN.Mother = PEOPLE.x
GROUP BY PEOPLE.x
HAVING Count(CHILDREN.x) >=
[Enter desired minimum number of children:];
- step 2: grandmothers with at least k children, each of which has n
children
PeopleHavingAtLeastkChildrenAndnGrandChildrenM: (7p)
SELECT PeopleHavingAtLeastkChildrenM.x,
PeopleHavingAtLeastkChildrenM.ChildrenNo,
Count(GrandChildren.x) AS GrandChildrenNo
FROM PEOPLE AS GrandChildren INNER JOIN (PEOPLE AS CHILDREN
INNER JOIN PeopleHavingAtLeastkChildrenM
ON CHILDREN.Mother = PeopleHavingAtLeastkChildrenM.x)
ON GrandChildren.Mother = CHILDREN.x
GROUP BY PeopleHavingAtLeastkChildrenM.x,
PeopleHavingAtLeastkChildrenM.ChildrenNo
HAVING Count(GrandChildren.x) >=
[Enter desired minimum number of grand children:];
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
240
- step 3: getting names and sorting
Test1-d2: (3p)
SELECT PEOPLE.Name,
PeopleHavingAtLeastkChildrenAndnGrandChildrenM.ChildrenNo,
PeopleHavingAtLeastkChildrenAndnGrandChildrenM.GrandChildrenNo
FROM PEOPLE INNER JOIN
PeopleHavingAtLeastkChildrenAndnGrandChildrenM
ON PEOPLE.x = PeopleHavingAtLeastkChildrenAndnGrandChildrenM.x
ORDER BY
PeopleHavingAtLeastkChildrenAndnGrandChildrenM.ChildrenNo
DESC,
PeopleHavingAtLeastkChildrenAndnGrandChildrenM.GrandChildrenNo
DESC, PEOPLE.Name;
Figure 6.5 Result of query Test1-d2 for k = n = 1 (1p)
Figure 6.6 Result (empty set) of query Test1-d2 for k = n = 2 (1p)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
241
6.2.2 Oracle solution
a.
INSERT INTO STATES (State, Country)
VALUES ('Argeș', 1); (0.5p)
INSERT INTO CITIES (City, State)
SELECT 'Curtea de Argeș', x FROM STATES
WHERE State = 'Argeș' AND Country = 1; (1.5p)
b.
ALTER TABLE PEOPLE ADD BurrialPlace NUMBER(6,0);
(0.5p)
ALTER TABLE PEOPLE ADD CONSTRAINT fkBurPlace
FOREIGN KEY (BurrialPlace) REFERENCES CITIES;
(1.25p)
UPDATE PEOPLE SET BurrialPlace = (SELECT x FROM
CITIES WHERE City='Curtea de Argeș') WHERE x IN
(6,7); (1.75p)
c.
-------------------------------------------------
-- DDL for View CITIES_STATES_CONTRIES
-------------------------------------------------
CREATE OR REPLACE FORCE VIEW
"LAB_DB"."CITIES_STATES_CONTRIES"
("X", "City, State, Country") AS
SELECT CITIES.x, City || ', ' || STATES.State ||
', ' || COUNTRIES.Country "City, State, Country"
FROM COUNTRIES INNER JOIN (STATES INNER JOIN
CITIES ON STATES.x = CITIES.State)
ON COUNTRIES.x = STATES.Country
ORDER BY City || ', ' || STATES.State || ', ' ||
COUNTRIES.Country; (3.5p)
d.
- step 0: create a temporary table for storing mothers with at least k
children (1p)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
242
-------------------------------------------------
-- DDL for Table TMPPHLKC
-------------------------------------------------
CREATE TABLE "LAB_DB"." TMPPHLKC"
( "X" NUMBER(6,0),
"CHILDRENNO" NUMBER(2,0)
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1
MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT
CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS";
- step 1: add to the LAB_DB_PL_SQL package the following procedure:
procedure AtLeastkChildrennGrandChildrnM
(Min_no_of_children in integer,
Min_no_of_grand_children in integer,
rc out GenericCursorType);
procedure AtLeastkChildrennGrandChildrenM
(Min_no_of_children in integer,
Min_no_of_grand_children in integer,
rc out GenericCursorType) is
begin
--0. initialize tmpPHLkC (0.5p) DELETE FROM tmpPHLkC;
--1. compute and store into it mothers with at least k
--children (4.5p) INSERT INTO tmpPHLkC
SELECT PEOPLE.x, Count(CHILDREN.x) ChildrenNo
FROM PEOPLE CHILDREN INNER JOIN PEOPLE
ON CHILDREN.Mother = PEOPLE.x
GROUP BY PEOPLE.x
HAVING Count(CHILDREN.x) >= Min_no_of_children;
--2. compute and return grandmothers with at least k
--children, each of which has n children (9p)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
243
OPEN rc FOR
SELECT NAMES.Name, ChildrenNo,
Count(GrandChildren.x) GrandChildrenNo
FROM (PEOPLE GrandChildren INNER JOIN (PEOPLE CHILDREN
INNER JOIN tmpPHLkC
ON CHILDREN.Mother = tmpPHLkC.x)
ON GrandChildren.Mother = CHILDREN.x) INNER JOIN PEOPLE
NAMES ON tmpPHLkC.x = NAMES.x
GROUP BY NAMES.Name, tmpPHLkC.ChildrenNo
HAVING Count(GrandChildren.x) >= Min_no_of_grand_children
ORDER BY ChildrenNo DESC, Count(GrandChildren.x) DESC,
NAMES.Name;
end AtLeastkChildrennGrandChildrnM;
- step 3: test for k = n = 1, respectively 2
var c refcursor;
exec LAB_DB_PL_SQL.AtLeastkChildrennGrandChildrnM(1,1,:c);
print c;
Figure 6.7 Result of stored procedure
AtLeastkChildrennGrandChildrnM for k = n = 1 (1p)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
244
exec LAB_DB_PL_SQL.AtLeastkChildrennGrandChildrenM(2,2,:c);
print c;
Figure 6.8 Result (empty set) of stored procedure
AtLeastkChildrennGrandChildrnM for k = n = 2 (1p)
6.3 Best practice rules
6.4 Homework H6.1. Prove that any acyclic binary, homogeneous relations (including
autofunctions) are also irreflexive, anti-symmetric and anti-transitive.
H6.2. Prove that any function is idempotent (that is f2 = f f = f) if and
only if it is transitive. Also it is anti-idempotent if and only if it is anti-
transitive.
H6.3. Prove that an autofunction is symmetric if and only if f2 is equal to
the unity mapping.
H6.4. Establish a similar characterization theorem for reflexive and anti-
reflexive functions.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
245
H6.5. Consider the REIGNS db scheme from the previous lecture
(including object sets RULERS, MARRIAGES, and REIGNS) and forma-
lize the following “business rules”, by using object constraints:
a) Nobody can get married, neither before he/she is born, nor after he/she
passed away.
b) No children may be born after their mother passed away for more than
one day.
c) No king, president, etc. can rule without being alive.
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
246
Volume II: Database
design
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
247
Chapter 7. 7th Lab: Business Analysis
7.1 Exercises P7.1 (Forward engineering)(FE)
Consider the sub-universe of a PC store and design a rdb for it such as to
allow storing data on its catalog of products and stocks, limited, to begin
with, at types, characteristics, quantities, manufacturers, and prices, by
applying the algorithms for business analysis assistance and for translat-
ing E-RDs and restriction lists into rdb schemes. Provide a plausible in-
stance for this rdb.
Solution:
Trivially, manufacturers and products are sets of objects; note that, just
like as bought/lent/borrowed/etc. books are copies of edition volumes,
products are categorized into types (e.g. notebooks, iPads, memory, etc.),
which is also a set of objects (even if abstract). Besides names, products
also have unique codes. Obviously, any product is of only one type and
made by only one manufacturer (whereas both types and manufacturers
include/make several products).
Consequently, the first half of this sub-universe should be modeled by the
following E-RD:
TypeName PROD_TYPES Code ProductName
Type
Manufacturer PRODUCTS
ManufactName MANUFACTURERS Price StockQty
Figure 7.1 First half of the E-RD for P7.1
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
248
The following business rules exist for this first half:
- Product manufacturer, product type, as well as manufacturer and
product type names, product name, code, price, and stock quantity
are compulsory.
- Manufacturer name, product type name, and product code unique-
ly identify the elements of the corresponding sets.
- Prices (let’s say in Euros, with maximum 2 decimals) and stocks
(naturals) are positive and not greater than, let’s say, 9999.
On characteristics, note first that there are several ones having same mea-
surement units (e.g. internal and external memories’ size, USBs, modems,
network adapters’ speed, etc.): consequently, it is a good idea to add such
an object set too. Then, same goes for product type characteristics: several
product types have same characteristics (e.g. size, speed, resolution,
weight, etc.), so that it is a good idea to abstract both an entity-type object
set CHARACTERISTICS and a relationship-type one PROD_TYPE_
CHARS relating it to the PROD_TYPES above. Moreover, in order to
store actual products characteristics values, a similar PRODUCTS_
CHARS relationship is needed too. Consequently, the second half of this
sub-universe should be modeled by the following E-RD:
PROD_TYPES PROD_TYPES_CHARS CharName
CHARACTERISTICS
MU
PRODUCTS PRODUCTS_CHARS MEASURE_UNITS
CharactValue MUName
Figure 7.2 Second half of the E-RD for P7.1
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
249
The following business rules exist for this second half:
- Characteristics and measurement units’ names, as well as values
of characteristics for products are compulsory (measurement units
for characteristics are not, as there are type-types ones too).
- Characteristics and measurement units’ names are uniquely identi-
fying their corresponding object sets elements.
Figure 7.3 shows the corresponding full structural E-RD.
Manufacturer Type
MANUFACTURERS PRODUCTS PROD_TYPES
PRODUCTS_CHARS PROD_TYPES_CHARS
CHARACTERISTICS
MU
MEASURE_UNITS
Figure 7.3 Structural E-RD for P7.1
The following restriction list is associated to it:
1. MANUFACTURERS
a. max. cardinality: 99 (R0)
b. attribute (co-)domains (ranges):
ManufactName: ASCII(32) (R1)
c. compulsory data: ManufactName (R2)
d. uniqueness: ManufactName (there does not exist two manu-
facturers having same names) (R3)
2. PROD_TYPES
a. max. cardinality: 99 (R4)
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
250
b. attribute (co-)domains (ranges):
TypeName: ASCII(16) (R5)
c. compulsory data: TypeName (R6)
d. uniqueness: TypeName (there may not be two product
types having same names) (R7)
3. PRODUCTS
a. max. cardinality: 9999 (R8)
b. attribute (co-)domains (ranges):
ProductName, Code: ASCII(16) (R9)
Price, StockQty: RAT+(6,2) (R10)
c. compulsory data: ProductName, Code, Price, StockQty,
Type, Manufacturer (R11)
d. uniqueness: Code (there does not exist two products
having same codes) (R12)
4. MEASURE_UNITS
a. max. cardinality: 99 (R13)
b. attribute (co-)domains (ranges):
MUName: ASCII(16) (R14)
c. compulsory data: MUName (R15)
d. uniqueness: MUName (R16)
5. CHARACTERISTICS
a. max. cardinality: 99 (R17)
b. attribute (co-)domains (ranges):
CharName: ASCII(16) (R18)
c. compulsory data: CharName, MU (R19)
d. uniqueness: CharName (R20)
6. PROD_TYPES_CHARS
a. max. cardinality: 99999 (R21)
b. compulsory data: ProductType, Characteristic (R22)
c. uniqueness: <ProductType, Characteristic> (it is sense-
less to store twice a same characteristic for
a same product type) (R23)
7. PRODUCTS_CHARS
a. max. cardinality: 99999 (R24)
b. attribute (co-)domains (ranges):
Christian Mancas & Alina Dicu, FILS, Bucharest Polytechnic Univ.: DB Lab. Notes
251
CharactValue: ASCII(16) (R25)
c. compulsory data: CharactValue, Product, Characteristic
(R26)
d. uniqueness: <Product, Characteristic> (there may not be two
values for a same characteristic for a same product) (R27)
Note that ProductType and Characteristic are the two canonical Cartesian
projections of PROD_TYPES_CHARS; as such, their values are compul-
sory and their product is minimally one-to-one. Same thing obviously is
true for Product and Characteristic of PRODUCTS_CHARS.
Also note that there is an associated non-relational type restriction,
namely:
“In order for a product to have a characteristic, the corresponding product
type should also have that characteristic”. (R28)
Figure 7.4 shows the corresponding rdb and a plausible instance.
Note that many beginners are not using the power of abstraction: instead
of PRODUCTS, PROD_TYPES, CHARACTERISTICS, PROD_TYPES_
CHARS, and PRODUCTS_CHARS, they are adding unrelated object sets
for each product type (e.g. NOTEBOOKS, PADS, MONITORS, etc.) and
attaching to each of them as attributes (properties) all characteristics that
apply. This means that instead of these above 7 tables, each having an
average of 4 columns and some 1,000 rows, their “dbs” have dozens of ta-
bles, each one having in average dozens of columns and only dozens of
rows. To avoid such mistakes, always remember the business analysis
(BA) “golden rules” BAR1 and BAR2.
Also note that they would most probably not abstract MANUFACTU-
RERS, PROD_TYPES, and MEASURE_UNITS either: instead, they are
adding text attributes Manufacturer, Type, and MU to all of their tables!
Obviously, besides violating BAR3, this has as very unpleasant effects
both insertion, update, and deletion anomalies.
252
MANUFACTURERS(x,
ManufactName)
x ManufactName
Auton(2) ASCII(32)
NOT NULL NOT NULL
1 Intel
2 Apple
3 Samsung
4 Dell
PROD_TYPES(x, TypeName)
x TypeName
Auton(2) ASCII(16)
NOT NULL NOT NULL
1 notebooks
2 monitors
3 memories
4 hard disks
PRODUCTS(x, Code)
x Code Product
Name
Pric
e
Stock
Qty
Type Manufacturer
Auto
n(4)
ASCII(1
6)
ASCII(1
6)
RAT+(6,2
)
RAT+
(6,2)
Im(PROD
_TYPES,x
)
Im(MANUFAC
TURERS,x)
NOT
NUL
L
NOT
NULL
NOT
NULL
NOT
NUL
L
NOT
NUL
L
NOT
NULL
NOT NULL
1 DI5520 INSPIR
ON
5520
$600 8 1 4
2 AMBA MacBoo
k Air
$120
0
4 1 2
3 SMSA4
60
SA460 $380 6 2 3
4 SSD7T
D250
MZ-
7TD250
$170 10 4 3
247
MEASURE_UNITS(x, MUName)
x MUName
Auton(2) ASCII(16)
NOT NULL NOT NULL
1 Kg
2 GHz
3 GB
4 TB
5 Inch
6 M
7 MB
8 KB
9 DPI
CHARACTERISTICS (x,
CharName)
x CharName MU
Auto-
n(2)
ASCII(16) Im(MEA-
SURE
_UNITS.x)
NOT
NULL
NOT
NULL
1 Weight 1
2 CPU speed 2
3 Disk size 4
4 HDD type
PROD_TYPES_CHARS(x, ProductType Characteristic)
x ProductType Characteristic
Auton(5) Im(PROD_TYPES.x) Im(CHARACTERISTICS.x)
NOT NULL NOT NULL NOT NULL
1 1 1
2 1 2
3 1 4
4 3 3
PRODUCTS_CHARS(x, Product Characteristic)
x Product Characteristic CharactValue
Auton(5) Im(PRO-
DUCTS.x)
Im(CHARAC-
TERISTICS.x)
ASCII(16)
NOT NULL NOT NULL NOT NULL NOT NULL
1 1 1 3.08
2 1 2 2.5
3 4 4 SSD
4 4 3 0.25
Figure 7.4 P7.1 rdb and a plausible instance
248
P7.2 (Reverse engineering)(RE)
Consider the attached rdb GeografieBD/GeographyDB and infer its relatio-
nal scheme, as well as corresponding E-RDs and restrictions list, by applying
corresponding algorithms for reverse engineering Access/Oracle dbs and
translating relational schemes into E-RDs and restriction lists.
Access Solution:
a. relational schemes Access dbs
When opening GeografieBD, you note that there are 42 tables in it; accord-
ing to the algorithm, you have to inspect it all in design mode; for example,
figure 7.5 shows the design view of table VECINATATIM:
Figure 7.5 Design view of the Access GeografieBD VECINATATIM table
You should note that this table has 5 columns (attributes): #VM (the surro-
gate autonumber primary key), Mare, TaraVecina, KmMal, and PunctCar-
dinal.
From Mare’s lookup row source:
SELECT [#M], Mare FROM MARI ORDER BY Mare;
249
it results that Mare is a foreign key referencing (the primary surrogate key
#M of) table MARI.
Note that, when foreign keys do not have associated lookup combo-boxes,
you can detect them in Access in at least two other ways, by using either:
1. the Relationships db tools:
Click on the Relationships icon from the Database Tools tab of the Ribbon:
Figure 7.6 Relationships of GeografieBD
When, such as in this case, there are too many relationships, click on the
Clear Layout icon of the Ribbon’s Relationship Tools Design tab and con-
firm your request:
250
Figure 7.7 Confirmation message for clearing the Relationships window
Next, click on the Ribbon’s Show Table icon and add the VECINATATIM ta-
ble:
Figure 7.8 Adding a table instance to the Relationships window
After clicking on the Add button, click on the Close one; next, click on the
Ribbon’s Direct Relationships icon (see figure 7.9): all (and only the) foreign
keys of VECINATATIM are shown.
251
Figure 7.9 Inspecting VECINATATIM foreign keys
252
2. or the metadata catalog table MSysRelationships:
Open the MSysRelationships table (a hidden, system table), which contains a
row for every db foreign key:
Figure 7.10 GeografieBD MSysRelationships instance fragment
You may sort it, for example, ascendingly on szObject (which stores table
names containing foreign keys) and you then find easily the two foreign keys
of : Mare and TaraVecina (in the szColumn column). Note that column
szReference stores corresponding referenced columns, while the szRe-
ferenceObject one stores corresponding referenced table.
Similarly, from TaraVecina’s lookup row source:
SELECT [#T], Tara FROM TARI ORDER BY Tara;
it results that TaraVecina is a foreign key referencing (the primary surrogate
key #T of) table TARI.
KmMal is a natural attribute taking values, according to its Validation Rule
property (from the General tab), into [1, 999999999].
Finally, PunctCardinal, is also natural taking values, according to its Row
Source property (from the Lookup tab), into [1, 9].
253
From the Required property of the General tab, it follows that Mare, Tara-
Vecina, and PunctCardinal are compulsory (trivially, being the primary key,
#VM is compulsory too, by definition).
By clicking on the Ribbon’s Indexes icon (of the Table Tools Design tab), we
find out that, besides its primary key, this table also has the semantic key (as
its Unique property is set to Yes) Mare TaraVecina.
Figure 7.11 Inspecting VECINATATIM keys
Figure 7.12 shows VECINATATIM table’s properties: as there is no valida-
tion rule, you should infer that this table scheme does not contain any tuple
(check) constraint.
Figure 7.12 Inspecting VECINATATIM table’s properties
254
Figure 7.13 presents the corresponding rdb, obtained by the above reverse
engineering of this table scheme, as well as its first instance row values.
VECINATATIM (#VM, Mare TaraVecina)
#VM Mare TaraVecina KmMal PunctCardinal
Auton. Im(MARI.#M) Im(TARI.#T) [1,
999999999]
[1, 9]
NOT
NULL
NOT NULL NOT NULL NOT NULL
1 13 1 4
Figure 7.13 Relational scheme of VECINATATIM and its first row
b. E-RDs + restriction lists relational schemes
Figure 7.14 shows the corresponding E-RD. Note that, as Mare and TaraVe-
cina are compulsory and the semantic key of this table is Mare TaraVe-
cina, you can infer that, in fact, VECINATATIM is a relationship: see equi-
valent corresponding E-RD in figure 7.15.25
PunctCardinal
Mare TaraVecina
MARI VECINATATIM TARI
KmMal
Figure 7.14 E-RD of VECINATATIM
25
Note that this is also obvious from this table instance: it stores pairs of type < c, s >, where
c is a country which is neighbor to a sea s, so TaraVecina and Mare are the corresponding
canonical Cartesian projections of VECINATATIM.
255
PunctCardinal
MARI VECINATATIM TARI
KmMal
Figure 7.15 Equivalent E-RD of VECINATATIM
The associated list of restrictions fragment is the following:
J. VECINATATIM
a. attribute (co-)domains (ranges):
KmMal: [1, 999999999] (Ri)
PunctCardinal: [1, 9] (Ri+1)
b. compulsory data: Mare, TaraVecina, PunctCardinal (Ri+2)
c. uniqueness: Mare TaraVecina (it is senseless to store
more than once that a country is neighbor to a sea) (Ri+3)
Oracle Solution:
a. relational schemes Oracle dbs
When opening GeographyDB, you note that there are 42 tables in it; accord-
ing to the algorithm, you have to inspect it all; for example, figure 7.16
shows the design view of table SNEIGHBORS.
Obviously, we could use in what follows the GUI of the SQL Developer, just
like we used Access’ one above (see homework 7.7); however, we prefer
here the following other two equivalent solutions:
querying Oracle’s metacatalog (see, for a similar Access solution,
homework 7.6);
extracting metadata from the corresponding SQL DDL statements.
Both of them are general, although also needing technological knowledge.
256
Figure 7.16 Oracle GeographyDB SNEIGHBORS table scheme
a.1 querying Oracle’s metacatalog
In order to find out what are the user tables of a db, run the following query
(see figure 7.17):
select TABLE_NAME, TABLESPACE_NAME, STATUS,
NUM_ROWS, TEMPORARY, READ_ONLY
from user_tables
where DROPPED = 'NO'
order by TABLESPACE_NAME, TABLE_NAME;
Figure 7.17 Querying Oracle’s metacatalogue for finding tables
257
Note that:
- user_tables is a system view (like all of the following data sources
from this sub-section)
- you may obtain dozens of other metadata on tables from this view
- the normal STATUS of tables is ‘VALID’
- NUM_ROWS gives you corresponding actual cardinalities
- fundamental tables have TEMPORARY = ‘N’.
Similarly, you can get metadata on user table columns by running the follow-
ing query (see figure 7.18):
select TABLE_NAME, COLUMN_NAME, DATA_TYPE,
DATA_LENGTH, DATA_PRECISION, NULLABLE,
COLUMN_ID, DATA_DEFAULT, CHARACTER_SET_NAME,
CHAR_LENGTH
from user_tab_columns
order by TABLE_NAME, COLUMN_NAME;
Figure 7.18 Querying Oracle’s metacatalogue for finding table columns
Note that:
- user_tab_columns is a system view
- you may obtain dozens of other metadata on columns from this view
- the actual (physical) order of columns within tables is stored in
COLUMN_ID
258
- DATA_LENGTH gives corresponding actual logical number of cha-
racters/digits
- DATA_PRECISION gives corresponding actual number of digits after
the decimal point (and is null for all other data types)
- NULLABLE = ‘N’ means that corresponding columns does not accept
null values
- CHARACTER_SET_NAME gives (for text type columns) the actual
alphabet used for storing corresponding values (with ‘CHAR_ CS’
meaning ASCII and null for non-text columns)
- CHAR_LENGTH gives for text columns (for numeric ones it is al-
ways 0!) the actual (physical) maximum number of bytes needed to
store corresponding column values (which is always equal to DA-
TA_LENGTH above for the ASCII alphabet, but, for example, four
times bigger for UNICODE).
Similarly, you can get metadata on user constraints by running the following
query (see figure 7.19):
select TABLE_NAME, CONSTRAINT_TYPE,
CONSTRAINT_NAME, SEARCH_CONDITION,
R_CONSTRAINT_NAME
from user_constraints
where INVALID is null AND STATUS = 'ENABLED'
order by TABLE_NAME, CONSTRAINT_TYPE,
CONSTRAINT_NAME;
Note that:
- user_constraints is a system view
- you may obtain many other metadata on constraints from this view
- figure 7.20 decodes constraint types codes given by CON-
STRAINT_TYPE
- SEARCH_CONDITION gives corresponding logic expressions for
check (tuple) and NOT NULL constraints (and null for the rest)
- R_CONSTRAINT_NAME gives the corresponding referenced prima-
ry/unique constraint name (note that constraint names are unique
within any db, just like for tables/views, etc.) for foreign keys
(referential integrities)( and null for the rest).
259
Figure 7.19 Querying Oracle’s metacatalogue for finding table constraints
Figure 7.20 Oracle’s constraint types
Finaly, you can get metadata on user constraint columns by running the fol-
lowing query (see figure 7.21):
select * from user_cons_columns
order by TABLE_NAME, CONSTRAINT_NAME,
COLUMN_NAME;
260
Figure 7.21 Querying Oracle’s metacatalogue for constraint columns
Note that:
- user_cons_columns is a system view
- POSITION gives corresponding column position in the constraint
(when applicable: e.g. in keys and foreign keys)
Obviously, you can combine all this data into only one result, also filtered by
a table name (see figure 7.22), for example with the following query:
select X.TABLESPACE_NAME, X.TABLE_NAME, STATUS,
261
NUM_ROWS, TEMPORARY, READ_ONLY,
X.COLUMN_NAME, DATA_TYPE, DATA_LENGTH,
DATA_PRECISION, NULLABLE, COLUMN_ID,
DATA_DEFAULT, CHARACTER_SET_NAME,
CHAR_LENGTH, CONSTRAINT_TYPE,
CONSTRAINT_NAME, SEARCH_CONDITION,
R_CONSTRAINT_NAME, POSITION
from
(select TABLESPACE_NAME, T.TABLE_NAME, T.STATUS,
NUM_ROWS, TEMPORARY, READ_ONLY,
A.COLUMN_NAME, DATA_TYPE, DATA_LENGTH,
DATA_PRECISION, NULLABLE, COLUMN_ID,
DATA_DEFAULT, CHARACTER_SET_NAME,
CHAR_LENGTH
from user_tables t inner join user_tab_columns a
on t.table_name = a.table_name
where DROPPED = 'NO' AND
t.table_name = 'SNEIGHBORS') x
full outer join
(select TABLESPACE_NAME, T.TABLE_NAME,
CONSTRAINT_TYPE, C.CONSTRAINT_NAME,
SEARCH_CONDITION, R_CONSTRAINT_NAME,
COLUMN_NAME, POSITION
from user_tables t inner join user_constraints c
on t.table_name = c.table_name inner join
user_cons_columns cc on c.constraint_name =
cc.constraint_name
where INVALID is null AND C.STATUS = 'ENABLED'
AND t.table_name = 'SNEIGHBORS') y
on x.table_name = y.table_name and
x.column_name = y.column_name
order by X.TABLESPACE_NAME, X.TABLE_NAME,
CONSTRAINT_TYPE, CONSTRAINT_NAME,
X.COLUMN_NAME;
262
Figure 7.22 Querying Oracle’s catalogue for SNEIGHBORS metadata
a.1 extracting metadata from corresponding SQL DDL statements
The easiest way to extract all SQL DDL statements pertaining to a table is to
right-click its name, select the Quick DDL option and then click on the Save
to Clipboard one:
Figure 7.23 Extracting SQL DDL statements for table SNEIGHBORS
Then, you can paste the result in any text file (including SQL Developer
worksheets); here is the one obtained for table SNEIGHBORS:
263
-------------------------------------------------
-- DDL for Table SNEIGHBORS
-------------------------------------------------
CREATE TABLE "LAB_DB"."SNEIGHBORS"
( "X" NUMBER(3,0),
"SEA" NUMBER(3,0),
"COUNTRY" NUMBER(3,0),
"SHOREKM" NUMBER(9,0),
"CARDINALPOINT" NUMBER(1,0)
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1
MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT
CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS" ;
COMMENT ON COLUMN "LAB_DB"."SNEIGHBORS"."X" IS
'Primary surrogate SNEIGHBORS key';
COMMENT ON COLUMN "LAB_DB"."SNEIGHBORS"."SEA" IS
'Neighbor sea (to country)';
COMMENT ON COLUMN
"LAB_DB"."SNEIGHBORS"."COUNTRY" IS 'Neighbor
country (to sea)';
COMMENT ON COLUMN
"LAB_DB"."SNEIGHBORS"."SHOREKM" IS 'Total number of
Km of country''s sea shore';
COMMENT ON COLUMN
"LAB_DB"."SNEIGHBORS"."CARDINALPOINT" IS 'Cardinal
point of the sea shore for the country';
COMMENT ON TABLE "LAB_DB"."SNEIGHBORS" IS 'The
set of pairs of interest <s,c> storing the fact
that sea s is neighbor to country c.';
-------------------------------------------------
-- DDL for Index TARI_PK
-------------------------------------------------
CREATE UNIQUE INDEX "LAB_DB"."TARI_PK" ON
"LAB_DB"."SNEIGHBORS" ("X")
PCTFREE 10 INITRANS 2 MAXTRANS 255
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1
MAXEXTENTS 2147483645
264
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT
CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS" ;
-------------------------------------------------
-- DDL for Index SNEIGHBORS_UK1
-------------------------------------------------
CREATE UNIQUE INDEX "LAB_DB"."SNEIGHBORS_UK1" ON
"LAB_DB"."SNEIGHBORS" ("COUNTRY", "SEA")
PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE
STATISTICS
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1
MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT
CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS" ;
-------------------------------------------------
-- Constraints for Table SNEIGHBORS
-------------------------------------------------
ALTER TABLE "LAB_DB"."SNEIGHBORS" ADD CONSTRAINT
"SNEIGHBORS_UK1" UNIQUE ("COUNTRY", "SEA")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
COMPUTE STATISTICS
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1
MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT
CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS" ENABLE;
ALTER TABLE "LAB_DB"."SNEIGHBORS" MODIFY ("X" NOT
NULL ENABLE);
ALTER TABLE "LAB_DB"."SNEIGHBORS" MODIFY ("SEA"
NOT NULL ENABLE);
ALTER TABLE "LAB_DB"."SNEIGHBORS" MODIFY
("COUNTRY" NOT NULL ENABLE);
ALTER TABLE "LAB_DB"."SNEIGHBORS" MODIFY
("CARDINALPOINT" NOT NULL ENABLE);
ALTER TABLE "LAB_DB"."SNEIGHBORS" ADD CONSTRAINT
"TARI_PK" PRIMARY KEY ("X")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
265
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1
MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT
CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS" ENABLE;
-------------------------------------------------
-- Ref Constraints for Table SNEIGHBORS
-------------------------------------------------
ALTER TABLE "LAB_DB"."SNEIGHBORS" ADD CONSTRAINT
"SNEIGHBORS_COUNTRIES_FK1" FOREIGN KEY ("COUNTRY")
REFERENCES "LAB_DB"."COUNTRIES" ("X")
ENABLE;
ALTER TABLE "LAB_DB"."SNEIGHBORS" ADD CONSTRAINT
"SNEIGHBORS_SEAS_FK1" FOREIGN KEY ("SEA")REFERENCES
"LAB_DB"."SEAS" ("X") ENABLE;
Obviously, this script too contains all needed metadata for RE table
SNEIGHBORS, just like the result shown in figure 7.22 does.
Finally, regardless of which above method we used, we can infer the follow-
ing metadata on the SNEIGHBORS table:
It has 5 columns (attributes): X (the surrogate autonumber primary
key), Sea, Country, ShoreKm, and CardinalPoint
Sea is a foreign key referencing (the primary surrogate key X of)
table SEAS
Country is a foreign key referencing (the primary surrogate key X of)
table COUNTRIES
ShoreKm is a natural attribute taking values into [1, 999999999]
CardinalPoint is also natural taking values into [1, 9]
Sea, Country, and CardinalPoint are compulsory (trivially, being the
primary key, X is compulsory too, by definition)
It also has the semantic key Country Sea
No check (tuple) constraint is defined on this table.
Figure 7.24 presents the corresponding rdb, obtained by the above reverse
engineering of this table scheme, as well as its first instance row values.
266
SNEIGHBORS (X, Sea Country)
x Sea Country ShoreKm Cardi-
nalPoint
Auto-
n(3)
Im(SEAS.X) Im(COUNTRIES.X) [1,
999999999]
[1, 9]
NOT
NULL
NOT NULL NOT NULL NOT
NULL
1 13 1 4
Figure 7.24 Relational scheme of SNEIGHBORS and its first row
b. E-RDs + restriction lists relational schemes
Figure 7.25 shows the corresponding E-RD. Note that, as Sea and Country
are compulsory and the semantic key of this table is Sea Country, you can
infer that, in fact, SNEIGHBORS is a relationship: see equivalent
corresponding E-RD in figure 7.26.26
CardinalPoint
Sea Country
SEAS SNEIGHBORS COUNTRIES
ShoreKm
Figure 7.25 E-RD of SNEIGHBORS
26
Note that this is also obvious from this table instance: it stores pairs of type < c, s >, where
c is a country which is neighbor to a sea s, so Country and Sea are the corresponding
canonical Cartesian projections of SNEIGHBORS.
267
CardinalPoint
SEAS SNEIGHBORS COUNTRIES
ShoreKm
Figure 7.26 Equivalent E-RD of SNEIGHBORS
The associated list of restrictions fragment is the following:
J. SNEIGHBORS
d. attribute (co-)domains (ranges):
ShoreKm: [1, 999999999] (Ri)
CardinalPoint: [1, 9] (Ri+1)
e. compulsory data: Sea, Country, CardinalPoint (Ri+2)
f. uniqueness: Sea Country (it is senseless to store
more than once that a country is neighbor to a sea) (Ri+3)
7.2 Best practice rules (BAR1) Use abstraction to obtain as few object sets (with as many
elements) and attributes as possible (dually: do not inflate the concept
sets, that is db schemes, but their instances).
(BAR2) Except for very few exceptions (e.g. db parameters), all ob-
ject sets should be related to at least one other object set.
(BAR3) Object names are character strings, but objects are not! For
example, people are not born or living or getting married in character
strings, but in cities; Apple, Intel, IBM, Microsoft are not character
strings, but corporations, etc.
7.3 Homework H7.0 Design at least one translation algorithm from rdbs to:
a. Access
b. Oracle
c. any other RDBMS of your choice
Implement the rdb from figure 7.4 above, by applying at least one of them.
268
H7.1 Extend the data model shown in figure 7.3 above for also storing the re-
lationships between measurement units (e.g. 1TB = 1024GB = 1024 *
1024MB = 1024 * 1024 * 1024KB), as well as family of products, product
models, product serial numbers, and product compositions (out of other
products; e.g. a notebook is made out of a motherboard, several CPUs,
memory chips, a HDD, etc.). Modify accordingly your above im-
plementation (see 7.0).
H7.2 Consider the sub-universe of a university and design a rdb for it such as
to allow storing data on its faculties, departments, streams, deans, dept.
heads, professors, students, curriculums, disciplines, credits, exams, and stu-
dents exam results, by applying the algorithms for business analysis assis-
tance and for translating E-RDs and restriction lists into rdb schemes. Pro-
vide a plausible instance for this rdb.
H7.3 Finish P7.2 above. Should the restrictions list also contain non-relatio-
nal type business rules? If yes, give at least three examples of different types.
H7.4 Design forward and reverse engineering algorithms for MySQL. Apply
them for P7.1 above and a corresponding rdb of your choice.
H7.5 Prove that for any binary mathematical relation the product of its two
canonical Cartesian projections is minimally one-to-one.
H7.6 Design an equivalent RE algorithm for inferring relational schemes
from Access dbs, by using only its metacatalogue (i.e. the set of system, hid-
den MSys prefixed tables).
H7.7 Design an equivalent RE algorithm for inferring relational schemes
from Oracle dbs, by using the SQL Developer GUI.
269
Chapter 8. 8th Lab: Elementary mathematic db schemes
8.1 Exercises
P8.1 Consider the following student attendances to classes sub-universe
description, E-RD, and restrictions list:
Enrolled students (SSN, first, and last name) may obtain marks (between 1
and 20) when attending scheduled (week day, start and end date, room
number) classes (calendar date) of disciplines (name and between 1 and 20
credit points), held by teachers (SSN, first, and last name). Trivially, classes
may not end before starting.
SSN FName LName
STUDENTS ATTENDANCES Mark
Date CLASSES
Schedule StartH
ROOMS SCHEDULES WeekDay
EndH
RoomNo
TEACHERS COMPETENCES DISCIPLINES
SSN FName LName Discipline Credits
Fig. 8.1: E-RD of student attendances to classes’ sub-universe
270
Associated restrictions list:
a. Maximum cardinalities
STUDENTS: 99999 (R1)
CLASSES, ROOMS, TEACHERS, DISCIPLINES: 999 (R2)
COMPETENCES, SCHEDULES: 9999 (R3)
ATTENDANCES: 999999 (R4)
b. Ranges
SSN: [1, 999999999] (R5)
FName, LastName, Discipline: ASCII(64) (R6)
Mark, Credits: [1, 20] (R7)
Date: [1 Oct. 2010, 30 May Year(SysDate()) + 1] (R8)
RoomNo: ASCII(8) (R9)
Weekday: [1, 5] (R10)
StartH: [8, 19] (R11)
EndH: [9, 20] (R12)
c. Compulsory data
SSN, FName, LastName, Discipline, Credits, Date, RoomNo,
StartH, EndH, Weekday, Schedule (R13)
d. Uniquenesses
SSN, RoomNo, Discipline (R14)
e. Other restrictions:
“no room may accommodate more than one class simultaneously” (R15)
“classes may not end before starting” (R16)
1. Apply the algorithm for translating E-RDs and restriction lists into
(elementary) mathematical schemes.
2. Refine the above obtained math scheme, by applying the algorithm
for assisting sets, functions, and constraints design.
3. Further refine the above obtained math scheme, by applying the al-
gorithm for assisting keys discovery.
4. Apply the algorithm for translating (elementary) mathematical
schemes into relational ones and non-relational constraint lists;
271
provide a plausible data instance for the obtained rdb with at least
two rows per table.
Solution:
1. Obviously, the corresponding initial (elementary) mathematical
scheme is the following:
STUDENTS (The set of all enrolled students since Oct. 1st 2010)
x NAT(5), total
SSN [1, 999999999], total
FName ASCII(64), total
LName ASCII(64), total
ROOMS (The set of all available classrooms of interest)
x NAT(3), total
RoomNo ASCII(8), total
TEACHERS (The set of all teachers teaching since at least Oct. 1st 2010)
x NAT(3), total
SSN [1, 999999999], total
FName ASCII(64), total
LName ASCII(64), total
DISCIPLINES (The set of all disciplines for which, since Oct. 1st 2010, there
were enrolled students)
x NAT(3), total
Discipline ASCII(64), total
Credits [1, 20], total
COMPETENCES = (TEACHERS, DISCIPLINES) (The set of pairs <t, d> of
interest storing the fact that, since at least Oct. 1st 2010, teacher t
teaches discipline d)
x NAT(4), total
SCHEDULES = (COMPETENCES, ROOMS) (The set of pairs <c, r> of
interest storing the fact that, since at least Oct. 1st 2010, teacher t
teaches discipline d, where c = <t, d>, in room r)
x NAT(4), total
Weekday [1, 5], total
272
StartH [8, 19], total
EndH [9, 20], total
SchedulesC: (x SCHEDULES)(StartH(x) < EndH(x)) (as per R16)
CLASSES (The set of all scheduled classes)
x NAT(3), total
Date [1 Oct. 2010, 30 May Year(SysDate()) + 1], total
Schedule : CLASSES SCHEDULES, total
ATTENDANCES = (STUDENTS, CLASSES) (The set of all student attendan-
ces of scheduled classes)
x NAT(6), total
Mark [1, 20]
2. Here are the results of applying the design assistance algorithm for:
a. Sets
a.1 No set is semantically overloaded (so no structural refinements are ne-
eded from this point of view). Please review chapter 2, where this sub-uni-
verse modeling is discussed too contrastively, as compared to a poorer so-
lution!
a.2 No set is a subset of another set (so no inclusion constraints need to be
added to the scheme).
a.3 No association-type set has arity greater than two (so none has to be re-
placed by its equivalent entity-type set and explicit structural keys and
functions corresponding to its canonical Cartesian projections).
a.4 All binary associations are non-functional:
- COMPETENCES is not functional, because there are teachers that
may teach several disciplines, as well as disciplines that may be
taught by several teachers.
- SCHEDULES is not functional, because there are teacher competen-
ces that may be taught in several rooms, as well as rooms that may
accommodate teaching of several teacher competences.
- ATTENDANCES is not functional, because there are students attend-
ing several classes, as well as classes attended by several students.
273
a.5 No association-type set is ill-defined, except SCHEDULES: for example,
our db labs are held in the same room (CJ201), in the same week day (Tues-
days), but at different hours (from 8h to 10h with group 1232E and from 10h
to 12h with subgroup 1231aE); consequently, the product of its two
canonical Cartesian projections Room Competence is not one-to-one, so
this is not a mathematic relation.
As such, you should replace it by an entity-type object set (and correspond-
ingly correct the E-RD):
SCHEDULES (The set of schedule entries since Oct. 1st 2010)
x NAT(4), total
Weekday [1, 5], total
StartH [8, 19], total
EndH [9, 20], total
Room : SCHEDULES ROOMS, total
Competence : SCHEDULES COMPETENCES, total
On the contrary, COMPETENCES is well defined as Teacher Discipline is
minimally one-to-one (it would be senseless to store more than once a same
pair <t, d>) and the same goes for ATTENDANCES, as Student Class is
minimally one-to-one too (it would be senseless to store more than once a
same pair <s, c>).
a.6 No association is homogeneous (so that we need not investigate reflexi-
vity, irreflexivity, symmetry, anti-symmetry, etc.).
b. Functions
b.1 All functions are well defined:
- people (be them students or teachers) have only one SSN, first, and last
name;
- classrooms, disciplines, and classes have only one number, name, credit
points, and calendar date, respectively;
- schedule entries have only one associated week day, start, and end hour;
274
- finally, in this simplified model, students may get only one mark for each
attended class (e.g. the average of all ones, if several are possible).
b.2 No other function (except for the unique ones) is one-to-one:
- FName : there may be several students/teachers having same first name.
- LName : there may be several students/teachers having same last name.
- Mark : there may be several students getting same mark for a same class
(and even a same student may get a same mark for different classes).
- Date: there may be several classes scheduled in a same room, even in a
same day (but at different start/end hours)
- Weekday: there may be several classes scheduled in a same week day, even
in a same classroom (but at different start/end hours)
- StartH: there may be several classes scheduled to start at a same hour (but
in different classrooms)
- EndH: there may be several classes scheduled to end at a same hour (but in
different classrooms)
- Credits: there may be several disciplines having same number of credit
points
- Schedule: there may be several classes scheduled in a same room for a
same discipline, held by a same teacher (typically, one per week,
during a semester).
On the contrary, all those declared as one-to-one are really one-to-one:
- SSN: there may not be two persons having same SSN
- Discipline: there may not be two disciplines having same name
- RoomNo: there may not be two classrooms having same number.
275
b.3 No function is onto, except for Schedule:
- Schedule is onto, because for any entry in the schedule there should
be at least one corresponding class (generally, there are one per
semester week). Consequently, the following constraint has to be
added to this db scheme:
Schedule : CLASSES SCHEDULES, total, onto
- Obviously, FName, LastName, Discipline, and RoomNo are not, as
not all possible ASCII character combinations of maximum 64/8
characters should be first or last person, disciplines, or room
names/numbers.
- SSN is not either, as not all possible combination of 9 digits is an
actual SSN.
- Mark and Credits are not either, as it is not compulsory that all pos-
sible marks/credits (i.e. all naturals between 1 and 20) be granted for
student class activities or crediting disciplines, respectively.
- Date is not either, as generally there are no classes during week-ends
or holidays.
- StartH is not either, as not all possible daily hour should be an actual
class start one.
- EndH is not either, as not all possible daily hour should be an actual
class end one.
- Weekday is not either, as not all possible week days should have
scheduled classes.
b.4 There are no bijective functions.
b.5 There are no auto-functions (so that we need not investigate reflexivity,
irreflexivity, symmetry, anti-symmetry, etc.).
b.6 There are no canonical surjections (representative systems).
b.7 There are no object sets having no totally defined functions.
c. Constraints
c.1 R15 (“no room may accommodate more than one class simultaneously”),
the only not formalized constraint left (as R16 was formalized by Sche-
276
dulesC above) has the following formalization (no two classes may overlap
in a same week day and same room):
C15: (x,ySCHEDULES)(Weekday(x) = Weekday(y) Room(x) =
Room(y)) (StartH(y) EndH(x) StartH(x) EndH(y))
Note that C15 also implies the following key constraints:
KC15a: Weekday Room StartH key (there may not be two schedules en-
tries having same week day, room, and start hour) and its dual
KC15b: Weekday Room EndH key (there may not be two schedules en-
tries having same week day, room, and end hour)
c.2 There are other constraints that apply in this sub-universe too, but are
missing from this model: C15 (which corresponds to the non-quantum phy-
sics law “no two objects may occupy the same space in the same time”) has
an obvious dual (corresponding to the non-quantum physics law of non-ubi-
quity: “no object may simultaneously occupy two distinct space slots”), as no
student/teacher may simultaneously attend more than one scheduled class.
Consequently, the following two constraints have to be added to this math
scheme:
C16: (x,ySCHEDULES)(Weekday(x) = Weekday(y) Competence(x) =
Competence(y)) (StartH(y) EndH(x) StartH(x) EndH(y)) (“no tea-
cher may teach simultaneously in several classrooms”)
C17: (<s,c>,<s,c’>ATTENDANCES) Date(Class(c)) = Date(Class(c’))
(StartH(Class(c’) EndH(Class(c)) StartH(Class(c)) EndH( Class(c’)))
(“no student may simultaneously attend several classes”)
Obviously, just like for C15, C16 also implies the following two key con-
straints:
KC16a: Weekday Competence StartH key (there may not be two sche-
dules entries having same week day, teaching competence, and start hour)
and its dual
277
KC16b: Weekday Competence EndH key (there may not be two sche-
dules entries having same week day, teaching competence, and end hour).
3. Applying the assistance algorithm for keys discovery yields the follow-
ing:
STUDENTS and TEACHERS
n = 2, as there are two not one-to-one mappings defined on it, both being
non-prime in this context (FName LName is not one-to-one, as
there may be several distinct persons having same first and last
names) no other semantic key exist.
ROOMS and COMPETENCES
n = 0 no other semantic key exist.
DISCIPLINES and ATTENDANCES
n = 1 (nothing to do, as according to b.2 above, neither Credits, nor Mark are
one-to-one) no other semantic key exist.
SCHEDULES
n = 5, as there are five not one-to-one mappings defined on it, all of them
being prime (see KC15a, KC15b, KC16b, and KC16b above): Week-
day, StartH, EndH, Competence, and Room.
i = 2:
- Weekday StartH key? No, because several classes may be scheduled in a
same week day to start at a same hour.
- Weekday EndH key? No, because several classes may be scheduled in a
same week day to end at a same hour.
- Weekday Competence key? No, because several classes may be scheduled
in a same week day for a same teacher and discipline.
- Weekday Room key? No, because several classes may be scheduled in a
same week day and in a same classroom.
- StartH EndH key? No, because several classes may be scheduled (even in
a same week day) to start and end at same hours.
- StartH Competence key? No, because several classes may be scheduled to
start at a same hour for a same teaching competence (but in different
week days).
278
- StartH Room key? No, because several classes may be scheduled to start
at a same hour in a same classroom (but in different week days).
- EndH Competence key? No, because several classes may be scheduled to
end at a same hour for a same teaching competence (but in different
week days).
- EndH Room key? No, because several classes may be scheduled to end at
a same hour in a same classroom (but in different week days).
- Competence Room key? No, because several classes may be scheduled in
a same classroom and for same teaching competences (but either in
different week days or in a same week day, but at different start and
end hours).
i = 3:
- Weekday StartH EndH key? No, because several classes may be sche-
duled in a same week day to start and end at same hours.
- Weekday StartH Competence key? Yes, according to KC16a.
- Weekday StartH Room key? Yes, according to KC15a.
- Weekday EndH Competence key? Yes, according to KC16b.
- Weekday EndH Room key? Yes, according to KC15b.
- Weekday Competence Room key? No, because several classes may be
scheduled in a same classroom for a same teaching competence (but
at different start and end hours).
- StartH EndH Competence key? No, because several classes may be
scheduled for same start and end hours, and for a same teaching com-
petence (but in different week days).
- StartH EndH Room key? No, because several classes may be scheduled
for same start and end hours, in a same classroom (but in different
week days).
- EndH Competence Room key? No, because several classes may be
scheduled for same end hours, in a same classroom, for a same tea-
ching competence (but in different week days).
i = 4:
The only non-superkey on this level is:
StartH EndH Competence Room key? No, because several classes may
be scheduled for same start and end hours, for a same teaching com-
petence, in a same classroom (but in different week days).
279
CLASSES
n = 2, as there are two not one-to-one mappings defined on it, both being
prime: Date Schedule is one-to-one, as there may not be several
distinct classes having same date and schedule and, as, according to
b.2 above, neither of them is one-to-one, this product is minimally
one-to-one, so this key has to be added to the model:
KClasses: Date Schedule key (there may not be several distinct classes
having same date and schedule entry)
4. Applying the algorithm for translating (elementary) mathematical
schemes into relational ones and non-relational constraint lists yields the
following rdb (to which a small plausible instance has also been added)
and non-relational constraints list:
STUDENTS (x, SSN)
x SSN FName LName
Auton(5) [1, 999999999] ASCII(64) ASCII(64)
NOT NULL NOT NULL NOT NULL NOT NULL
1 123456789 Ion Popescu
2 987654321 Pop Ionescu
TEACHERS (x, SSN)
x SSN FName LName
Auton(3) [1, 999999999] ASCII(64) ASCII(64)
NOT NULL NOT NULL NOT NULL NOT NULL
1 135792468 Dumitru Georgescu
2 975318642 George Dumitrescu
DISCIPLINES (x, Discipline)
x Discipline Credits
Auton(3) ASCII(64) [1, 20]
NOT
NULL
NOT
NULL
NOT
NULL
1 Algebra 12
2 Logic 16
ROOMS (x, RoomNo)
x RoomNo
Auton(3) ASCII(8)
NOT NULL NOT NULL
1 CB105
2 CJ201
280
COMPETENCES (x, Teacher Discipline)
x Teacher Discipline
Auton(3) Im(TEACHERS.x) Im(DISCIPLINES.x)
NOT NULL NOT NULL NOT NULL
1 2 1
2 1 2
SCHEDULES (x, Weekday StartH Competence, Weekday EndH
Competence, Weekday StartH Room, Weekday EndH Room)
StartH < EndH
x Competence Room Week-
day
StartH EndH
Auton(4) Im(COMPETEN-
CES.x)
Im(ROOMS.x) [1, 5] [8, 19] [9, 20]
NOT
NULL
NOT NULL NOT NULL NOT
NULL
NOT
NULL
NOT
NULL
1 1 1 1 10 12
2 1 2 2 8 10
CLASSES (x, Date Schedule)
x Schedule Date
Auton(3) Im(SCHEDULES.x) [1 Oct. 2010, 30 May Year(SysDate())
+ 1]
NOT NULL NOT NULL NOT NULL
1 1 Oct. 7, 2013
2 1 Oct. 14, 2013
ATTENDANCES (x, Student Class)
x Student Class Mark
Auton(6) Im(STUDENTS.x) Im(CLASSES.x) [1, 20]
NOT NULL NOT NULL NOT NULL
1 1 1
2 1 2 20
281
The associated non-relational constraints list is the following:
C15: (x,ySCHEDULES)(Weekday(x) = Weekday(y) Room(x) =
Room(y)) (StartH(y) EndH(x) StartH(x) EndH(y))
C16: (x,ySCHEDULES)(Weekday(x) = Weekday(y) Competence(x) =
Competence(y)) (StartH(y) EndH(x) StartH(x) EndH(y))
C17: (<s,c>,<s,c’>ATTENDANCES) Date(Class(c)) = Date(Class(c’))
(StartH(Class(c’) EndH(Class(c)) StartH(Class(c)) EndH(Class(c’)))
C18: Schedule : CLASSES SCHEDULES, onto
8.2 Best practice rules
8.3 Homework H8.1 Modify the above model on all three levels (E-R, mathematical, and re-
lational) accordingly, such as to:
a. abstract a single PERSONS set instead of STUDENTS and TEACHERS
b. consider that some classes may not be hold weekly, but only once in every
two weeks, either in the odd or in the even ones
c. consider that students may get several marks for any class, but only one
per type (e.g. homework, test, etc.)
d. add buildings, campuses, classroom types (e.g. computer science, physics,
chemistry, etc.), and teacher availabilities (per odd/even/weekly days and
time intervals)
e. implement the above obtained rdb by using a RDBMS version of your
choice.
H8.2 Design and develop (in any high level programming language you
choose) a software application based on the above db for managing it (which
includes enforcing of all non-relational constraints), as well as for assisting
class scheduling.
282
Chapter 9. 9th Lab: Analyzing E-RD cycles
9.1 Exercises
Consider the GeografieBD / GeographyDB db.
P9.1 Analyze the following autofunctions:
a. SuperSea : SEAS SEAS27
(y = SuperSea(x) means that sea y includes
sea x; for example, the Ionic, Adriatic, Aegean, etc. seas are included in the
Mediterranean one)
b. OrbitCenter : ASTRONOMIC_BODIES ASTRONOMIC_BODIES28
(y
= OrbitCenter(x) means that astronomic body y orbits around astronomic bo-
dy x; for example, all 67 Jupiter’s moons are orbiting around it, while it or-
bits around the Sun, which orbits around the center of the Milky Way, which
is the supermassive black hole called Sagittarius A, etc.)
Solution:
a. SuperSea
The corresponding E-RD cycle is shown in figure 9.1:
SEAS SuperSea
Figure 9.1: E-RD of the autofunction SuperSea : SEAS SEAS
Obviously, SuperSea models a tree-like hierarchy of seas, so it should be
acyclic29
: no sea may be included in itself, neither directly or indirectly
the SuperSea acyclic constraint has to be added to the corresponding db
scheme.
b. OrbitCenter
The corresponding E-RD cycle is shown in figure 9.2:
27
SuperMare : MARI MARI in the Romanian version. 28
CentruOrbita : CORPURI_CERESTI CORPURI_CERESTI in the Romanian version. 29
Which obviously means (see homework H6.1 and H6.2) that SuperSea is also irreflexive,
anti-symmetric, anti-transitive, and anti-idempotent.
283
ASTRONOMIC_BODIES OrbitCenter
Figure 9.2: E-RD of the autofunction OrbitCenter : ASTRONOMIC_BODIES
ASTRONOMIC_BODIES
As there are, for example, binary star systems (in which each star is orbiting
around the other: for example, in the binary system J0806, situated at about
1,600 light-years away from Earth, two white dwarf stars orbit one another e-
very 321 seconds), this autofunction does not model a tree-like hierarchy (i.e.
cycles in the OrbitCenter ‘s graph are not only possible, but cannot be pre-
vented without loss of information).
- reflexive? No, as, trivially, no astronomic body may orbit around itself.
- irreflexive? Yes, as such implausibilities should be banned from db storing
the OrbitCenter irreflexive constraint has to be added to the cor-
responding db scheme.
- symmetric? No, as there are, for example, planets (e.g. Earth) orbiting
around stars (e.g. Sun) that are not orbiting around their planets.
- anti-symmetric? No, as there are, for example, binary star systems (in
which each star is orbiting around the other).
- transitive? Yes, physically: as we’ve already seen above, any astronomic
body x orbiting around y that is orbiting round z is also orbiting
around z; but, as this information is computable (moreover, even
easily), it should not be stored (especially as there are myriads of
celestial bodies and the corresponding transitive closure would take a
tremendous amount of disk storage).
- anti-transitive? Yes, see transitivity above why the OrbitCenter anti-
transitive constraint has to be added to the corresponding db scheme.
P9.2 Analyze the following homogeneous binary association: DISTANCES =
(CITIES, ALL_CITIES)30
(where ALL_CITIES is a synonym for CITIES:
ALL_CITIES = CITIES) (The set of interesting pairs <c, c’> storing the aerial
minimum distance, in km, between cities c and c’).
Solution:
The corresponding E-RD cycle is shown in figure 9.3:
30
DISTANTE = (LOCALITATI, TOATE_LOCALITATILE) in the Romanian version.
284
DISTANCES
CITIES
Figure 9.3: E-RD of the homogeneous binary association DISTANCES
- reflexive? No, although, trivially, the distance between any city and itself is
perfectly defined and always 0 km, but this is computable, so there is
no need to store it.
- irreflexive? Yes, as such trivialities should be banned from db storing
the DISTANCES irreflexive constraint has to be added to the corres-
ponding db scheme.
- symmetric? No, although, trivially, the distance between any two cities is
perfectly defined in both ways and corresponding values are always
equal between them, but, once one of them is stored, the other
(symmetric) one is (very easily!) computable, so there is no need to
store it too.
- anti-symmetric? Yes, as, generally, computable data should be banned from
db storing the DISTANCES anti-symmetric constraint has to be
added to the corresponding db scheme.
- transitive? It depends, as, obviously, on one hand, given distances between
any two pairs of cities <x, y> and <y, z>, the distance between x and z
is perfectly defined (and one possible value for it, not necessarily the
minimum one, is, trivially, the sum of them); on the other, the
following three implementation solution types are possible, each of
them with its advantages and disadvantages:
as even if not that easy (because all existing paths between two cities
have to be discovered, their corresponding distances computed, and
the minimum one of them chosen, which is an instance of the well-
known problem of finding the least expensive path between two
nodes in a weighted graph), computing minimum distances between x
and z is always possible, so it should be banned from db storing; ob-
viously, this means that a DISTANCES anti-transitive constraint has
to be added to the corresponding db scheme and that the db size is
kept to the minimum, but both updating (because of the overhead for
enforcing this constraint) and querying data are slowed down;
in order to speedup querying (at the expense of a larger db size, but
not that important), we might dually opt for enforcing the DISTAN-
CES transitive constraint instead: of course that this should be tran-
sparent to users, as it can be automatically done by the application
managing the db (at each insertion of a new pair, update of an exist-
285
ing distance, and deletion of an existing pair, an intelligent algorithm
may automatically insert new corresponding pairs or delete existing
ones, in order to keep the graph transitive, as well as automatically
update computed distances; note that this imply also storing, for each
pair, a computed Boolean indicating whether or not the pair is a cal-
culated one or not and preventing users from deleting computed pairs
or updating their elements; on the contrary, users should have the
right to update computed distances, which, trivially, should trigger
changing the corresponding status of the corresponding pairs from
“computed” to “fundamental” and re-computing all other involved
computed distances); obviously, the overhead for enforcing this con-
straint is more expensive than for its above dual one;
a mixed solution is also possible and is, most probably, the best: none
of the above two constraints is enforced, i.e. users may store both
computable and not computable pairs; querying is first searching for
an existing stored pair and is computing desired value (when possi-
ble!) only when such data is not stored; this type of solution obvious-
ly is a compromise between the above two ones, as db size is larger,
but not as large as in the previous case, querying time is faster than in
the first case, but not that fast as in the second, and there is no con-
straint enforcing overhead; if db size is not a concern (which, gene-
rally, should not be the case for such a table), but querying speed is
(which is almost the rule), this solution might be greatly improved by
running after updates, during idle periods (e.g. night-time, week-
ends), a program for computing the transitive closure of this graph, as
well as corresponding minimum distances (but without any distinc-
tions between “fundamental” and “computed” pairs and no constraint
whatsoever on users’ rights on computed data).
P9.3 Analyze the following conventional commutative-type cycles:
a. Region : STATES REGIONS,31
StateCapital : STATES CI-
TIES,32
and RegionCapital : REGIONS CITIES.33
b. Country : ISLANDS COUNTRIES,34
and ISLANDS_SHARING =
(ISLANDS, COUNTRIES)35
, where Country : ISLANDS COUN-
TRIES stores the country which owns the whole island or the majori-
ty part of it.
31
Regiune : JUDETE REGIUNI in the Romanian version. 32
Capitala : REGIUNI LOCALITATI in the Romanian version. 33
Resedinta : JUDETE LOCALITATI in the Romanian version. 34
Tara : INSULE TARI in the Romanian version. 35
OCUPARE_INSULE = (INSULE, TARI) in the Romanian version.
286
c. Continent : STATES CONTINENTS,36
Country : STATES
COUNTRIES,37
and Continent: COUNTRIES CONTINENTS.38
Solution:
a. The corresponding E-RD cycle is shown in figure 9.4:
REGIONS
Region RegionCapital
STATES CITIES
StateCapital
Figure 9.4: E-RD of a (conventional) commutative-type function diagram
Obviously, the associated commutativity problem is RegionCapital Region
?= StateCapital, that is “should any state capital also be the capital of the re-
gion to which that state belongs (if any)?”.
Trivially, this is not the case, as, on one hand, regions are generally
made out of several states (and there is only one region capital) and, on the
other, region capitals are not always state capitals too. Consequently, this
diagram should not commute.
The associated anti-commutative problem is (xSTATES)(Region-
Capital Region(x) ? StateCapital(x)), that is “should never a state capital
also be the capital of the region to which that state belongs (if any)?”.
Trivially too, this is not the case either, as (in fact, very often) region
capitals are also state capitals.
Consequently, this cycle is uninteresting, as it should not either com-
mute or anti-commute: no constraints are involved.
b. The corresponding E-RD cycle is shown in figure 9.5:
Country Island
COUNTRIES ISLANDS_SHARING ISLANDS
Country
Figure 9.5: E-RD of a (conventional) commutative-type function diagram
36
No such function exists in the dbs (neither in the English Oracle one, nor in the Romanian
Access one, for reasons that are explained in the solution of this exercise). 37
Tara : JUDETE TARI in the Romanian version. 38
Continent : TARI CONTINENTE in the Romanian version.
287
Obviously, the associated commutativity problem is Country Island ?=
Country (where Country : ISLANDS_SHARING COUNTRIES and Island
: ISLANDS_SHARING ISLANDS are the canonical Cartesian projections
of ISLANDS_SHARING), that is “should any country (also) sharing an island
be the one that owns the whole island or the majority part of it?”.
Trivially, this is not the case, as there are islands shared by several
countries (e.g. Ireland, split between the Irish Republic, owning more than
three quarters of it, and the U.K., owning its Northern Ireland).
The associated anti-commutative problem is (xISLANDS_SHAR-
ING)(Country Island(x) ? Country(x)), that is “should never a country that
owns the whole island or the majority part of it also be stored as sharing a
part of it?”.
Trivially, this is the case, as no same data should be stored more
than once in a db.
Consequently, this diagram should anti-commute and the corres-
ponding (xISLANDS_SHARING)(Country Island(x) Country(x)) con-
straint should be added to the db scheme.
c. The corresponding E-RD cycle is shown in figure 9.6:
CONTINENTS
Continent Continent
STATES COUNTRIES
Country
Figure 9.6: E-RD of a (conventional) commutative-type function diagram
Obviously, the associated commutativity problem is Continent Country ?=
Continent, that is “should any state belong to the continent to which belongs
the country to which that state belongs?”.
Trivially, this should always be the case: this diagram conventionally
commutes (that is Continent Country = Continent). But, as Continent :
STATES CONTINENTS is computable from the other two functions, the
best data modeling solution is to eliminate it from the db scheme, thus break-
ing this cycle.39
39
Obviously, if querying speed is a concern, as this data is almost static once entered and as
additional disk space is not that important, a computed function *Continent = Continent
Continent Country might be added to the db for storing in STATES corresponding conti-
nent names (provided that it is implemented as a controlled data redundancy: either invisible
or read-only for users, and immediately re-computed by the managing applications or
RDBMS triggers whenever involved data is updated).
288
P9.4 Analyze the following unidirectional cycle: State : CITIES
STATES,40
Region : STATES REGIONS, and RegionCapital : REGIONS
CITIES.
Solution:
The corresponding E-RD cycle is shown in figure 9.7:
REGIONS 1REGIONS
Region RegionCapital
1STATES STATES CITIES 1CITIES
State
Figure 9.7: E-RD of a unidirectional (local commutative-type) function
diagram
Trivially, as this cycle has three nodes, we have to investigate all correspond-
ing three possible local commutativities:
1. RegionCapital Region State ?= 1CITIES (should any city be the capital
of the region from which makes part the state to which the city belongs?)
Obviously, this is not possible, as regions include several states, each
one having several cities, whereas they only have one capital.
2. Region State RegionCapital?= 1REGIONS (should the capital of any re-
gion belong to a state belonging to that region?)
Obviously this is true, as no region may have as its capital a city be-
longing to another region (dually, all region capitals belong to the corres-
ponding regions).
Consequently, the corresponding local commutativity constraint Re-
gion State RegionCapital = 1REGIONS should be added to this db scheme.
3. State RegionCapital Region ?= 1STATES (should any state include the ca-
pital of the region to which belongs?)
Obviously no, as there is only one region capital per region and as
any city belongs to only one state.
P9.5 Analyze the following cycle: Ocean : HARBORS CONTINENTS,41
City : HARBORS CITIES,42
State : CITIES STATES, Country :
NEIGHBORSOC COUNTRIES,43
Ocean : NEIGHBORSOC CONTI-
40
Judet : LOCALITATI JUDETE in the Romanian version. 41
Ocean : PORTURI_OCEANICE CONTINENTE in the Romanian version. 42
Localitate : PORTURI_OCEANICE LOCALITATI in the Romanian version. 43
Tara : VECINATATIOT TARI in the Romanian version.
289
NENTS44
, where NEIGHBORSOC is the set of interesting pairs of the form
<o,c> storing the fact that country c is neighbor to ocean o (note that CONTI-
NENTS ‘ instance is partitioned into two blocks by the values of its Boolean
function Ocean?: (land) continents and oceans (“water continents”)), where
Country : NEIGHBORSOC COUNTRIES and Ocean : NEIGHBORSOC
CONTINENTS are the canonical Cartesian projections of NEIGH-
BORSOC.
Solution:
The corresponding E-RD cycle is shown in figure 9.8:
Ocean
CONTINENTS HARBORS
Ocean City
NEIGHBORSOC CITIES
Country State
COUNTRIES STATES
Country
Figure 9.8: E-RD of a (generalized commutative-type) function diagram
The cycle has two source (HARBORS and NEIGHBORSOC) and two desti-
nation (CONTINENTS and COUNTRIES) nodes, so it is of the third type.
Let us consider any two elements of its sources: h HARBORS and n
NEIGHBORSOC; the (generalized commutativity) question is: in what cir-
cumstances should Ocean(h) = Ocean(n) and/or Country(n) = Country(State(
City(h)))?
Obviously, in order for a country to have an oceanic harbor, it has to be
neighbor to the corresponding ocean, which means that, for any harbor h,
there always should be a pair n such that both above equalities hold; conse-
quently, this diagram has the following generalized commutativity that
should be added to the db scheme:
OceanHarborsConstraint: (hHARBORS)(nNEIGHBORSOC)(Ocean(
h) = Ocean(n) Country(n) = Country State City(h)).
44
Ocean : VECINATATIOT CONTINENTE in the Romanian version.
290
9.2 Best practice rules 0. Do not ignore, but enforce any existing constraint of the modeled sub-
universe (as not enforcing it allows storing implausible data).
1. Do not enforce any constraint that does not apply to the modeled sub-
universe (as enforcing it prevents from storing plausible data).
2. When enforcing both an existing constraint P or its dual not P (anti-P)
is possible, always choose the best alternative from the users’ point of
view (which is, generally, best possible overall db application speed); some-
times, especially when it is possible to run additional programs during idle
periods (e.g. computing transitive closures), the best solution is to not en-
force either of them.
3. For any autofunction f or homogeneous binary association (math
relation) r that model a tree-like hierarchy, add constraint f / r acyclic to
the corresponding db scheme.
4. For acyclic autofunction or homogeneous binary association (or math
relation) corresponding irreflexivity, anti-symmetry, anti-transitivity,
and/or anti-idempotency should not also be added to the db scheme, as
they are all implied by acyclicity.
5. Generally, do not add to your db scheme any implied constraint.
6. For any autofunction or homogeneous binary association (or math
relation) that are not acyclic, corresponding reflexivity, irreflexivity,
symmetry, anti-symmetry, transitivity, anti-transitivity, idempotency,
and/or anti-idempotency should be investigated.
7. Trivially, if any object set has a constraint P, then it cannot also have con-
straint not P (anti-P); consequently, if P was discovered, not P should not
be investigated anymore.
8. Do not store in dbs computable data; if storing it might significantly
help (for increasing overall processing speed), then store it as a controlled
redundancy (i.e. read-only for users and automatically re-computed imme-
diately after its value changed).
9. Do not allow storing same data more than once in a same db.
291
10. Whenever an E-RD function diagram should commute and one of
the involved functions is computable out of all others, break the corres-
ponding cycle by eliminating from your model the computable one.45
11. Whenever a commutative-type function diagram is not commutative,
you should investigate whether or not it should anti-commute.
12. For any unidirectional E-RD cycle, you should look for possible local
commutativities in all of its nodes.
13. Do not ignore any E-RD cycle: they have to be declared uninterest-
ing (constraint free) only after thorough investigations, done according
to the algorithm for assisting E-RD cycles analysis.
9.3 Homework H9.1 Using the algorithm for assisting E-RD cycles analysis, discover and
analyze all the
a. remaining cycles of the GeografieBD / GeographyDB db;
b. cycles in all remaining examples from both chapter 2, as well as pre-
vious lectures and labs.
45
Obviously, when significant faster processing speed is possible and critical, controlled re-
dundancy might be used in such cases too – see the eighth best practice rule above.
292
Chapter 10. 10th Lab: Enforcing non-relational constraints Recall that RDBMSs are generally providing only the following 5 types of
(relational) constraints:
1. (co-)domain (range)
2. NOT NULL (mandatory, required) (totality)
3. Keys (uniqueness) (minimal one-to-oneness)
4. Foreign keys (referential integrity) (typed inclusion dependencies)
5. Tuple (check)
All other constraint types have to be enforced either by extended SQL trig-
gers or/and by event-driven high-level programming (generally, embedding
SQL) methods.
10.1 Prerequisites in Access: Forms and Event-Driven Methods Natively, Access does not provide extended SQL: only programmatically,
through ADO, may triggers (more generally, stored procedures and func-
tions) be declared and used, which, unfortunately, is very difficult to main-
tain and debug.
Fortunately, Access provides an event-driven embedding SQL high-level pro-
gramming language called VBA (Visual Basic for Applications), as well as
(Windows) forms and a reports generator.
Forms and reports are bi-dimensional objects, to which data sources (tables
or queries) may be attached, made out of a GUI and an associated VBA class.
The GUI mainly displays and allows users to modify data, but also provides
menus, (linked) sub-forms/reports, buttons, etc. Associated VBA classes pro-
vide both event-driven and ordinary methods (subroutines and functions) that
are used for enforcing constraints, performing calculations, implementing
business logic, etc.
For example, in order to create a datasheet type form on the COUNTRIES ta-
ble from our lab db containing all of its columns, click on the Form Wizard
icon from the Create ribbon tab, then select table COUNTRIES (see figure
10.1), then click on the “>>” button to select all columns (see figure 10.2),
then on Next; then click on the Datasheet radio button (see figure 10.3), click
on Next, and finally on Finish (see figure 10.4).
293
Figure 10.1 Selecting the data source of an Access form
Figure 10.2 Selecting the columns from the data source of an Access form
294
Figure 10.3 Selecting the GUI type of a standard Access form
Figure 10.4 Naming, saving, and opening of a newly created Access form
295
Figure 10.5 shows the newly created form COUNTRIES:
Figure 10.5 The COUNTRIES datasheet form
10.1.1 Forms/reports GUI: controls and properties
From the GUI point of view, forms and reports are collections of controls of
various types, having lot of attached properties. The most used control types
are the labels, text, list, and check boxes, sub-forms/reports, tabs, buttons
(both ordinary and radio), charts, hyperlinks, lines, rectangles, images, and
attachments. Figure 10.6 shows the design view of form COUNTRIES.
Properties are partitioned into four categories: data, event, format, and other.
As usual, the implicit property of any control is its value. Forms and reports
themselves have associated properties, just like their controls. From the data
events point of view, db instance rows are associated to forms/reports,
whereas their cells (corresponding column values for the current row) are
associated to controls.
Data properties include the db table/column (data source), table-type proper-
ties (e.g. default values, validation rules and texts, combo boxes lookup ones,
input masks, etc.)46
, concurrency access locking type (current record, all re-
cords, none), access rights (read-only, insert, update, delete, filtering), filters,
ordering, etc. Figure 10.7 shows the data properties of the COUNTRIES
form; for getting properties, right-click on the upper-left corner of the form.
46
generally the same as those of the corresponding tables, but not necessarily, case in which
they take precedence over table ones
296
Figure 10.6 The COUNTRIES datasheet form design view
Figure 10.7 The COUNTRIES form data properties
297
In order to make a (generally, computed, but not only) column read-only, set
(either statically or programmatically) its data Locked property to Yes.
Format properties include visibility (i.e. controls may be hidden), fonts, si-
zes, colors, alignments, special effects, etc.
Other properties include control names and captions, control tips and status
bar texts, popup, modal, menu bars, help files, etc.
Event properties store associated event procedures or embedded macros
handlers. Event chaining should be mastered; for example, the most usual
chain is form/report open, data load, followed by a loop of cursor positioning
on the current rows, an inner loop for the current controls, associated key-
board and mouse events, before and after control (cell) data in-
sert/update/delete, end of controls loop, before and after form (row) data in-
sert/update/delete, end of rows loop, data unload, and form/open close.
Event types are specific to control types. Figure 10.8 shows how to attach an
event-driven procedure to the On Current event of form COUNTRIES.
Figure 10.8 Attaching an event procedure to the COUNTRIES form
298
The most important and frequently used event types are the following:
10.1.1.1 Forms/reports associated event types
1. Open
Occurs whenever form/report opening is asked for, either through GUI or
programmatically (more precisely, when the corresponding object is instan-
tiated).
Can be cancelled.
Used mainly for denying opening or passing parameters, dynamically speci-
fying associated data source, filtering, and/or sorting.
2. Load
Occurs whenever forms/reports successfully open and have associated data
to load.
Cannot be cancelled.
Used mainly for filtering and sorting the data source and giving dynamically
computed titles to reports.
3. Current (Row)
Occurs whenever the cursor arrives on another data source row.
Cannot be cancelled.
Used mainly for storing initial row data into memory variables.
4. Before Insert
Occurs whenever user types for the first time something in a data control and
the current row is the new, blank one.
Can be cancelled.
Used mainly for dynamically rejecting undesired inserts into the data source.
299
5. After Insert
Occurs whenever a new row is appended in virtual memory to the data
source.
Cannot be cancelled.
Used mainly for retrieving corresponding auto-number value just generated
for the newly inserted row.
6. Before Update
Occurs whenever user modified at least one control data and asks for saving
current row values into the db (either explicitly, through GUI or programma-
tically, or implicitly, by trying to leave the current row).
Can be cancelled.
Used mainly for enforcing non-relational constraints involving at least two
columns of the data source.
7. After Update
Occurs whenever saving current row values into the db finished successfully.
Cannot be cancelled.
Used mainly for re-computing data sources, combo boxes, and programmati-
cally computed controls, as well as for any other data synchronizations bet-
ween simultaneously opened forms.
8. Delete
Occurs whenever user asks (either through GUI or programmatically) dele-
tion of the current row (or several selected rows).
Can be cancelled.
Used mainly for asking users context-sensitive deletion confirmations,
checking whether or not deletion is relationally possible (i.e. no referential
integrity would be violated), displaying corresponding context-sensitive error
messages when not, and enforcing non-relation constraints.
300
9. Before Delete Confirmation
Occurs whenever deletion request was not canceled by the Delete event (see
8 above).
Can be cancelled.
Used mainly for asking Access not to ask for user’s delete confirmation
(when this was already done by the Delete event).
10. After Delete Confirmation
Occurs whenever Access finishes to process a row(s) delete request.
Cannot be cancelled.
Used mainly for finding out whether or not the row(s) were actually deleted
and, in the affirmative case, for re-computing data sources, combo boxes,
and programmatically computed controls, as well as for any other data syn-
chronizations between simultaneously opened forms.
11. Click / Double-click
Occurs whenever user is clicking/double-clicking anywhere on the form, ex-
cept for its controls.
Can be cancelled for Double-Click, but cannot for Click.
Used mainly for triggering methods (for data synchronization, navigation,
etc.) in datasheet (tabular) forms (because buttons cannot be placed on them)
or rejecting corresponding request (for Double-Click).
12. Error
Occurs whenever a run-time error occurs.
Cannot be cancelled.
Used mainly for distinguishing between different possible causes of an error
(e.g. between different keys that would have been violated, as Access is only
indicating that a key violation was rejected, but not exactly for which key),
301
for managing errors, and/or for replacing context-independent standard sys-
tem errors with context-dependent ones.
13. No Report Data
Occurs whenever the data source of a report is an empty set.
Can be cancelled.
Used mainly for denying empty report preview/print.
14. Close
Occurs whenever form/report closing is asked for, either through GUI or pro-
grammatically.
Can be cancelled.
Used mainly for denying closing or asking a request confirmation.
10.1.1.2 Controls associated event types
1. Enter
Occurs whenever the cursor arrives on another control.
Cannot be cancelled.
Used mainly for storing initial control data into a memory variable.
2. Before Update
Occurs whenever user modified corresponding control data and tries to move
to another control of the same form/report or to another source data row.
Can be cancelled.
Used mainly for enforcing non-relational constraints involving correspond-
ing column of the data source.
3. After Update
Occurs whenever saving current control value into the virtual memory buffer
associated to the current row finished successfully.
302
Cannot be cancelled.
Used mainly for re-computing data sources, combo boxes, and programmati-
cally computed controls, as well as for any other data synchronizations bet-
ween simultaneously opened forms.
4. Click and Double-Click
Occurs whenever user is clicking/double-clicking on the corresponding con-
trol.
Can be cancelled for Double-Click, but cannot for Click.
Used mainly for triggering methods (for data synchronization, navigation,
etc.) or rejecting corresponding request (for Double-Click).
10.1.1.3 Combo boxes associated Not In List event type
Occurs whenever the associated Limit to List property has the value Yes and
user is entering in the corresponding control another value than the ones pro-
vided by the combo box values set.
Cannot be cancelled.
Used mainly for dynamically accepting new values in the corresponding
combo box and saving them into the db.
10.1.1.4 Invisible properties
Besides the above four property categories that are visible through the forms/
reports GUI, there also exists invisible control properties.
One of the most frequently used such property is the OldValue one, which
stores the initial value of any data bounded control (the one still stored in the
db during updates, up to after saving into the db the newly updated corres-
ponding ones).
Another one, also heavily used, is the NewRecord one, which is associated
only to forms that have associated data sources and which is True only when
the current row is the distinguished blank one (used for adding new rows to
the current data source instance) and False otherwise.
303
10.1.2 Forms/reports VBA classes: event-driven and ordinary methods
Only the VBA classes that are attached to forms or reports (and which have
names of the type Form_formName or Report_reportName, respectively)
may contain event-driven methods; such methods should have names of the
form Form_eventTypeName, Report_eventTypeName, or controlName_e-
ventTypeName, respectively; they are automatically launched whenever a
corresponding event occurs, but they may be called by any method too, just
like the ordinary ones; no other method is launched automatically. All event-
driven methods are of subroutine type. Some have no parameter, others have
input, output, and/or input-output ones. Ordinary methods47
may also be
functions.
10.1.2.1 The Cancel parameter of event-driven methods
Methods corresponding to event types that are cancelable have a distin-
guished Cancel integer (but used as Boolean) input-output parameter; when
such a method is automatically launched, the system is passing False as its
value; when the method ends and relinquishes control to the system, if Can-
cel remained False then the system goes on with normal processing of the
current event; otherwise, the current event is aborted and, moreover, the form
/report cursor is blocked in the current control and on the current data source
row. Consequently, assigning True to Cancel is the only way to deny form/
report opening/closing and reject undesired inserts/deletes/updates.
Here are the corresponding methods: Form_Open, Form_BeforeInsert, Form
_BeforeUpdate, Form_Delete, Form_BeforeDelConfirm, Form_DblClick,
controlName_DblClick, controlName_BeforeInsert, controlName_BeforeUp-
date, Report_NoData, Form_Close.
10.1.2.2 Other parameters of event-driven methods
Obviously, signatures of event-driven methods should match exactly corres-
ponding system’s requirements. The best thing to do is to always let Access
automatically generate such signatures.
Here are the names, types, contexts, semantics, and most used values of the
rest of the event-driven methods’ parameters:
47
Classes not associated to forms or controls, as well as modules (libraries) only contain or-
dinary methods.
304
1. Response As Integer
Type: Output
Most frequently used values:
- acDataErrContinue: for event cancelling (in comboBoxName_NotIn-
List rejects adding NewEntry in the combo box’s instance; in Form_
BeforeDelConfirm cancels system deletion confirmation message dis-
play; in Form_ Error cancels current error)
- acDataErrAdded: accepts adding NewEntry in the combo box’s in-
stance (in comboBoxName_NotInList)
2. Status As Integer
Type: Input
Most frequently used value: acDeleteOK, which signals that at least
one row has been deleted from the data source (in Form_AfterDel-
Confirm)
3. NewData As String
Type: Input
Values: entered by user in the corresponding combo box and which
does not belong to the combo box’s instance (in comboBoxName_
NotInList)
4. DataErr As Integer
Type: Input
Values: corresponding system error codes (in Forms_Error).
10.1.3 VBA programming techniques and tips
The VBA environment can be opened by typing Alt+F11. Variables are de-
clared just like in the legacy programming language Basic, with the Dim(en-
sion) keyword. Comments are introduced by an apostrophe and displayed in
green; keywords are displayed in blue; the rest of the correct code is dis-
played in black; erroneous code is displayed in red. To continue code on ano-
ther line, finish the current one with “ _” (a space followed by an under-
score); comments cannot be continued. VBA is not case sensitive; tip: always
write in lower case; if your code is correct, when you finish your line, the
system will automatically change in blue all recognized keywords and to up-
percase all db object, class, constant, variable, method, etc. referenced
names’ letters, as per their corresponding definitions.
305
10.1.3.0 Methods and variables scope
Procedures and variables that are declared with the keyword Private or
without any scope keyword are known only locally, in the corresponding
class or library (module); those declared with the keyword Public are known
to all classes and modules.
10.1.3.1 The Variant data type
The distinguished data type Variant can accommodate any value type and is
the only one that can store nulls too. Consequently, you can actually work
only with variant variables, which is, however, highly unadvisable, as it
takes lot of memory and significantly slows down the interpretation speed;
only use it temporarily when nulls are possible; then, any not value should be
moved to a correspondingly typed variable.
Note that any non-declared variable is assigned the Variant type and that the
default operating mode of the VBA environment in this respect is not to re-
quire variable declarations. As a best practice rule, do not forget to turn this
feature on for every project since the beginning: click on the Tools menu op-
tion, then on Options from the corresponding sub-menu, on Require Variable
Declaration check-box, and, finally on the OK button of the Options window
(see figure 10.9).
Figure 10.9 Turning on the Require Variable Declaration VBA option
306
10.1.3.2 The programming errors system
The programming errors system is not activated by default; except for excep-
tional cases (to be introduced below), you should activate it explicitly at the
beginning of any method and immediately after you don’t need it to be sus-
pended anymore (in the exceptional cases) with the statement On Error
GoTo err_point, where err_point should be the label of an errors pro-
cessing section of the method (generally, the last one, which has to be prece-
ded by an Exit Sub/Function statement and should contain at least, for
displaying the errors’ messages, the following statement: MsgBox Err.
Source & "->" & Err.Description, vbCritical, "Error
in method currentMethodName of class currentClass-
Name..."). Figure 10.10 shows the VBA class associated to form COUN-
TRIES, after adding the error system to the Form_Current method.
Figure 10.10 The VBA programming environment
Note that the error object Err is a stack of occurred runtime error codes
whose implicit property is the code of the most recently encountered error;
its Source property contains the name of the processor that discovered the er-
ror (e.g. the SQL engine, ADO, the VBA interpreter, etc.); its Description pro-
perty contains the corresponding error description. The assignment Err = 0
pops the stack. The stack is empty when Err = 0.
307
You can ignore all errors immediately after executing On Error Resume
Next and up to the next On Error GoTo in the sequence or the end of
the method. Use Resume Next in the exceptional cases when browsing
object collections (e.g. open tables, queries forms, reports, etc.) to decide
whether or not a particular object is opened or not.
10.1.3.3 Message and input boxes
You can display messages by using the statement or function MsgBox (see
the example above for displaying error messages); both of them have the fol-
lowing three parameters, in this order:48
- a text to be displayed in the center of the message box;
- a numeric code for the icons and buttons to be displayed in the upper left
corner of the message box and below the above test, respectively;
- a text to be displayed in the title of the message box (if you don’t provide it,
“Microsoft Access” is displayed as title).
Note that you always should use system predefined constants for the numeric
code; for example, vbQuestion + vbOkCancel + vbDefault-
Button2 asks for displaying the question mark icon, an Ok button, and a
Cancel default one.
A best practice programming rule is to always consistently use icons as fol-
lows (note that displaying an icon is not mandatory):
- vbQuestion for questions
- vbCritical for critical errors
- vbExclamation for warnings (not critical errors)
- vbInfo for all other information.
You have the following seven possible button combinations:
- vbOkOnly or no button constant Ok
- vbMsgBoxHelpButton Ok and Help
- vbOkCancel Ok and Cancel
48
Actually, there are other two parameters as well for specifying associated custom help and
context.
308
- vbRetryCancel Retry and Cancel
- vbYesNo Yes and No
- vbYesNoCancel Yes, No, and Cancel
- vbAbortRetryIgnore Abort, Retry, and Ignore.
By default, the first button is the default one; when displaying the Cancel or
No buttons as well, a good practice is to always make them the default one
by also adding to the numeric parameter the constant vbDefaultButton2;
obviously, constant vbDefaultButton3 is making the third one be the default;
trivially, you don’t need either vbDefaultButton1 or vbDefaultButton4; note
that if you use a constant that references a non-existing button position (e.g.
vbDefaultButton4), then the first one is made the default.
Besides icons, buttons, and their positions, through the numeric constant you
can also set other properties of the message box (e.g. make application or
even the system modal while the message box is displayed, display the mes-
sage box from right to left, etc.).
When only the Ok button is displayed, you don’t need to know which button
has been pressed, so you may use the MsgBox statement; when several but-
tons are displayed you may find out which one has been pressed by using the
MsgBox function instead: the code of this button is returned; corresponding
associated constants (e.g. vbOk, vbCancel, vbYes, vbNo, etc.) should be used
instead of hard-coded codes.
For example, the following statement is displaying the message box from fi-
gure 10.11 and then, depending on the button that was pressed, displays ei-
ther True or False (note that, just like in SQL, & is the strings concatenation
operator):
if vbCancel = MsgBox(“Are you sure you want to delete the “ _
& “ selected persons from the PEOPLE table?”,
vbQuestion + vbOKCancel + vbDefaultButton2,
“Please confirm or cancel your request…”) then
MsgBox “True”
else
MsgBox “False”
309
Figure 10.11 Example of a message box
Whenever you need to get from users only one data value, you may use the
InputBox function, which displays a window similar to the MsgBox one, but
only with the buttons OK and Cancel and a text box for inputting data, takes
as first parameter the text box label string, the title as the second, and the de-
fault value as the third,49
and returns a string containing the entered value; if
no value is entered and there is no default one, or when the Cancel button is
pressed instead of OK, it returns an empty string.
For example, the following statement is displaying the message input box
from figure 10.12; if user modifies 2013 in 2014 (see figure 10.13) and then
presses OK, string variable v will be assigned the value “2014”:
v = InputBox("Year:", "Please specify desired year ..." _,
2013)
10.1.3.4 ADO DB recordsets
Sometimes, you need to process programmatically sets of db data values,
either only by reading them from and/or writing them to dbs. ADO DB
recordsets are the best way to store and process in main memory rdb table
and query instances.
10.1.3.4.1 ADODB connections
In order to connect to a db through ADO from high-level programming lan-
guages you need to declare and instantiate an ADODB connection. In VBA,
declaration of such a connection (say conn) is as follows:
49
Besides these three ones there are other optional four: two for specifying (in pixels) the
absolute position on the screen of the upper left corner of the input message window (by
default, this window is automatically centered) and the last two for specifying associated
custom help and context (just like for MessageBox above).
310
Dim conn As ADODB.Connection
Corresponding instantiation statement (which also opens conn) is very sim-
ple if the db is the current one (if not, just like in any other language, you
have to provide a connection string containing the name of the corresponding
data processor, your credentials, etc.):
Set conn = Application.CurrentProject.Connection
As soon as you (temporarily or definitively) don’t need such a connection
anymore, be sure to close it with:
conn.Close
You can re-open it anytime with:
conn.Open
As soon as you definitively don’t need such a connection anymore, be sure
not only to close it first, but also to free corresponding memory then with:
Set conn = Nothing
10.1.3.4.2 ADODB recordsets
In order to store db data into main memory and/or write main memory data
to dbs through ADO, you have to declare an ADODB recordset, instantiate,
and populate it with data. In VBA, declaration of such a recordset (say rs) is
as follows:
Dim rs As ADODB.Recordset
Corresponding instantiation statement is as follows:
Set rs = New ADODB.Recordset
Corresponding populating statement with data from an ADODB connection
(say conn), according to a SQL SELECT … statement is as follows:
rs.open “SELECT …”, conn
Among other properties, recordsets have a BOF (Begin Of File) and a EOF
(End Of File) ones; when both are true, the recordset is empty; if it is not
311
empty, you are generally processing it sequentially, in a forward manner, in a
While Not rs.EOF … rs.MoveNext Wend loop, where MoveNext
moves the recordset cursor to the following row (if any).
You can reference the value of the cell stored in the current recordset rs row
in its column c with rs!c.
Note that there are many other recordset methods and properties; for exam-
ple: MovePrevious, MoveFirst, MoveLast, AddNew, Bookmark, CancelUp-
date, Clone, CompareBookmark, Delete, EditMode, Filter, Find, GetRows,
GetString, Index, NextRecordSet, Requery, Resync, Save, Seek, Sort, Update,
UpdateBatch.
As soon as you (temporarily or definitevely) don’t need such a recordset any-
more, be sure to close it with:
rs.Close
Note that closing (de-populating) is compulsory before re-opening (that is re-
cordset overwriting is not allowed). You can re-open it anytime (not necessa-
rily from the same connection and/or the same populating SQL SELECT
statement) with another or the same rs.Open
As soon as you definitively don’t need such a recordset anymore, be sure not
only to close it first, but also to free corresponding memory then with:
Set rs = Nothing
10.1.3.5 ADO objects hierarchy: Me and Parent
ADO objects are structured in a hierarchy rooted in the Application object
node. Separators between nodes of a same branch are either dots or exclama-
tion marks; as dots are also used as separators between objects and their pro-
perties and methods, a best practice rule is to rather use exclamation marks.
There is an ADO objects collection that you will use the most frequently of
them all: Forms, the collection of all currently open forms. For example, the
path to a control C from (open) form F is Forms!F!C; the path to a control C’
from subform S of F is Forms!F!S!C’.
312
In (and only in) classes associated to forms or reports, you can abbreviate all
path prefixes up to the current (sub-)form/report with Me (the equivalent of
Java’s this), whereas those up to the parent one with Parent; for example,
from class Form_F, Forms!F!C can be abbreviated as Me!C, whereas Forms!
F!S!C’ as Me!S!C’; from class Form_S, Forms!F!C can be abbreviated as
Parent!C, whereas Forms!F!S!C’ as Me!C’.
10.1.3.6 Miscellanea
SQL is embedded in VBA not only for ADODB recordsets; for example, you
can run INSERT, UPDATE, and DELETE queries against the current db by
using the DoCmd.RunSQL command, which accepts as its only parameter a
string (containing such a DML statement) and passes it to the Access relatio-
nal engine for execution.
Note that, generally, the DoCmd (Do Command) object has very many other
methods (e.g. open/close dbs, tables, queries, forms, reports, web pages,
functions, stored procedures; set object properties; beep; apply filters;
select/copy/delete/save objects, dbs, files; runs programs/commands; etc.).
Another way of embedding SQL into VBA is offered by the Execute state-
ment and methods. For example, only with the Execute metod of an ADODB
connection object is possible to define explicit transactions, to commit, and
rollback them.
10.1.4 VBA and SQL Functions
10.1.4.1 Data access functions
When you need to read data cells from the db, the simplest and fastest way is
to use VBA’s data access functions, which can also be used in SQL. You
were already taught (see problem P2.1 from section 2.2.2 above) how to use
the most frequently used one of them, namely DLookup:
DLookup(“ expr”, “tbl/query_name”[, “criteria”]) is equivalent to: SELECT TOP 1 expr FROM tbl/query_name [WHERE criteria];
Recall that whenever the corresponding SELECT statement returns an empty
set, DLookup returns null.
313
Figure 10.12 Example of an input message box
Figure 10.13 Entering data into an input message box
Other frequently used ones are the following:
DMin(“expr”, “tbl/query_name”[, “criteria”]) is equivalent to: SELECT Min(expr) FROM tbl/query_name [WHERE criteria];
DMax(“expr”, “tbl/query_name”[, “criteria”]) is equivalent to: SELECT Max(expr) FROM tbl/query_name [WHERE criteria];
Note that both above functions are applicable only on numeric, alphanume-
ric, and date data sets.
DSum(“expr”, “tbl/query_name”[, “criteria”]) is equivalent to: SELECT Sum(expr) FROM tbl/query_name [WHERE criteria];
DAvg(“expr”, “tbl/query_name”[, “criteria”]) is equivalent to: SELECT Avg(expr) FROM tbl/query_name [WHERE criteria];
Note that both above functions are applicable only on numeric data sets.
DCount(“expr”, “tbl/query_name”[, “criteria”]) is equivalent to: SELECT Count(expr) FROM tbl/query_name [WHERE criteri-
a];
314
DCount doesn't count rows that contain Null values in the column referenced
by expr, unless expr is the wildcard character (* - asterisk). If you use an as-
terisk, the DCount function calculates the total number of rows, including
those that contain Null fields.
If expr contains several columns, separate their names with a concatenation
operator: either the ampersand (&) or the addition (+) ones. If you use the
ampersand to separate columns, the DCount function returns the number of
rows containing data in any of the mentioned columns. If you use the addi-
tion operator,50
the DCount function returns only the number of rows con-
taining data in all of the mentioned columns.
For all above functions:
expr includes the column(s) for which you want to apply the corres-
ponding function; it can be a string expression mentioning columns in
a table or query, or it can be an expression that performs a calculation
on data in those columns; besides names of columns from a table or
query, expr also accepts form controls, constants, and functions;
functions can be either built-in or user-defined, but not domain or
SQL aggregate ones;
tbl/query_name is a string expression identifying the set of rows that
constitutes the data set (domain); it can be or evaluable to a table or a
query name; queries that require parameters are not accepted;
they return null if the corresponding instance is empty, or none of its
data set (domain) rows are satisfying criteria, or if at least one co-
lumn from criteria is not one from tbl/query_name;
DMin, DMax, DSum, and DAvg ignore Null values in the column referenced
by expr.
Note that if you use DCount, DMin, DMax, DSum, or DAvg in SQL, values
are evaluated before data is grouped; if you use corresponding Count, Min,
Max, Sum, or Avg instead, data is grouped before values in the column(s) ex-
pression are evaluated.
50
The ampersand is the preferred operator for performing string concatenation: you should
avoid using the addition operator for anything other than numeric addition, unless you speci-
fically wish to propagate Nulls through an expression.
315
10.1.4.2 Other libraries functions
Note that all above library functions (as well as much more others – see Ap-
pendix 2) belong to the Access standard libraries Visual Basic for Applica-
tions, Microsoft Access Object Library, and Microsoft Office Access data-
base engine Object Library. Additionally, you may use functions from very
many other available libraries by simply including them into your project:
click on the Tools menu option, then on References from the corresponding
sub-menu, on desired library check-boxes, and, finally on the OK button of
the References window (see figure 10.14). In cases where several selected li-
braries have common functions you can set the search priority between libra-
ries by using the up and down Priority arrow buttons.
Note that ADO functions are not available by default and that there are three
such libraries: Microsoft ActiveX Data Objects, Microsoft ActiveX Data Ob-
jects Recordset, and Microsoft ADO ext. for DDL and Security. This allows
keeping project sizes to the minimum possible (e.g. if you don’t need to pro-
grammatically create or alter tables, constraints, users, and/or groups of
users, etc., then you don’t need the third one).
Figure 10.14 Adding libraries to your project
316
10.2 Exercises in Access Consider the Geografie db; using VBA, enforce the following non-relational
constraints:
P10.1 Any country should have as its capital a city from that country (dually:
no country may have as its capital a city of another country).51
Solution:
First, note that cities (LOCALITATI) belong to states (JUDETE) and these
belong to countries (TARI). Currently, the corresponding lookup RowSource
property of the combo-box Capitala from TARI reads:
SELECT LOCALITATI.[#L], [Loc] & " " & [Judete].[Judet]
AS [Loc Judet]
FROM JUDETE INNER JOIN LOCALITATI
ON JUDETE.[#J] = LOCALITATI.Judet
ORDER BY [Loc] & " " & [Judete].[Judet];
Obviously, this is computing all world’s cities, for any of its countries. The
best solution is the following preventing-type one: when the current country
changes, filter cities so that the combo-box contain only those of the current
country (which is stored in the [#T] control of the Form_TARI and Form_
TARIperTara forms). The corresponding modified SQL statement is:
SELECT LOCALITATI.[#L], [Loc] & " " & [Judete].[Judet]
AS [Loc Judet]
FROM JUDETE INNER JOIN LOCALITATI
ON JUDETE.[#J] = LOCALITATI.Judet
WHERE Tara = Me![#T]
ORDER BY [Loc] & " " & [Judete].[Judet];
In order to modify in VBA the RowSource property with this new value, the
corresponding Form_Current event-driven methods have to contain the fol-
lowing statement (note that all quotes in the SELECT statement have to be
replaced by apostrophes or double-quotes and that Me![#T] should be kept
outside the string, in order to be computed dynamically: otherwise, it would
be considered as a parameter and Access will ask users for its values on each
row!):
Me!Capitala.RowSource =
51
Formally, this a local commutativity constraint: Capitala Tara Judet = 1TARI (see P9.4)
317
"SELECT LOCALITATI.[#L], [Loc] & ‘ ‘ & [Judete].[Judet]
AS [Loc Judet]
FROM JUDETE INNER JOIN LOCALITATI
ON JUDETE.[#J] = LOCALITATI.Judet
WHERE Tara = " & Me![#T] &
" ORDER BY [Loc] & ‘ ‘ & [Judete].[Judet];"
For example, when the cursor arrives in any of these forms on the row cor-
responding to Romania (for which [#T] = 1), Capitala.RowSource will have
the value:
SELECT LOCALITATI.[#L], [Loc] & " " & [Judete].[Judet] AS [Loc Judet]
FROM JUDETE INNER JOIN LOCALITATI
ON JUDETE.[#J] = LOCALITATI.Judet
WHERE Tara = 1
ORDER BY [Loc] & " " & [Judete].[Judet];
When the cursor arrives in any of these forms on the row corresponding to
U.S.A. (for which [#T] = 4), Capitala.RowSource will have the value:
SELECT LOCALITATI.[#L], [Loc] & " " & [Judete].[Judet] AS [Loc Judet]
FROM JUDETE INNER JOIN LOCALITATI
ON JUDETE.[#J] = LOCALITATI.Judet
WHERE Tara = 4
ORDER BY [Loc] & " " & [Judete].[Judet];
For accordingly requerying the Capitala combo-box, the following statement
has to be added next:
Me!Capitala.Requery
In conclusion, enforcing this local commutativity constraint only needs two
VBA statements (with the first one embedding a SQL statement); but, before
jumping to actual coding, let’s not ever forget that any instance also has a
distinguished blank row (for inserting a new row into the corresponding in-
stance); obviously, as [#T] is null for this line, the execution of the corres-
ponding statement, which is:
SELECT LOCALITATI.[#L], [Loc] & " " & [Judete].[Judet]
AS [Loc Judet]
FROM JUDETE INNER JOIN LOCALITATI
ON JUDETE.[#J] = LOCALITATI.Judet
WHERE Tara =
ORDER BY [Loc] & " " & [Judete].[Judet];
318
would result in a runtime error, as the syntax … WHERE Tara = ORDER
BY … is not acceptable!
Obviously, as a new country cannot have cities belonging to it, Capitala
should be empty on the blank row (which is identifiable by the True value of
the NewRecord property of any form having a data source, whereas, for any
other of its rows, this property has value False); a very simple way to obtain
the empty set from any table/view is to add in the WHERE clause P and
NOT P, for any predicate P; consequently, for the blank line, the Capitala.
RowSource could have the following value:
SELECT [#L]
FROM LOCALITATI
WHERE Judet = 0 AND Judet <> 0;
By also adding comments and the error system, the Form_Current method of
both Form_TARI and Form_ TARIperTara forms should be the following:
'*************************
Private Sub Form_Current()
'*************************
'enforces constraint "No country may have as its capital a
'city of another country"
On Error GoTo err_point
If Me.NewRecord Then
Me!Capitala.RowSource = "SELECT [#L] FROM LOCALITATI " _
& " WHERE Judet = 0 AND Judet <> 0;"
Else
Me!Capitala.rowSource = "SELECT [#L], Loc, JUDETE.Judet " _
& "FROM JUDETE INNER JOIN LOCALITATI ON JUDETE.[#J]=" _
& "LOCALITATI.Judet WHERE Tara=" & Me![#T] _
& " ORDER BY Loc, JUDETE.Judet;"
End If
Me!Capitala.Requery
Exit Sub
err_point: MsgBox Err.Source & "-->" & Err.Description, _
vbExclamation, _
"Error in class Form_TARI . method Form_Current..."
End Sub
319
P10.2 In order for a country to have an oceanic harbor, it has to be neighbor
to the corresponding ocean (see the generalized commutativity constraint
OceanHarborsConstraint from P9.5 above).52
Solution:
First of all, obviously, the involved form is Form_PorturiOceanice, having
as data source the table PorturiOceanice, which stores the corresponding
pairs <[#PO], Ocean>; as OceanicHarbors (PorturiOceanice) is a subset of
CITIES (LOCALITATI), its primary key [#PO] is a foreign key too, referenc-
ing the primary key [#L]; Ocean is also a foreign key, referencing the prima-
ry key [#C] of CONTINENTE.
Unfortunately, as this constraint involves both of these columns, preventive-
type solutions are not applicable, so that we have to devise a cure-type one:
users will be free to choose any city in [#PO] and any ocean in Ocean, and,
before saving such a pair (either after updating an existing one or inserting a
new one), we will verify whether or not a corresponding pair <c, o> exists in
the VECINATATIOT (NEIGHBORSOceansCountries) table, where c is the
country to which city having id Me![#PO] belongs and o = Me!Ocean.
Obviously, if such a pair exists, then we do not have anything to do (as
OceanHarborsConstraint is not violated by this new pair); if not, in order to
enforce OceanHarborsConstraint, we could either reject this pair and ask
user to first add a corresponding pair <c, o> in the VECINATATIOT table,
which is not “nice” or add that pair automatically, with a user prior approval.
Also note that the PorturiOceanice table also has another column, called
PunctCardinal; this means that users might update only it, so that the Ocean-
HarborsConstraint should be enforced only when at least one of the two in-
volved columns, [#PO] and Ocean, are actually updated (that is they have
other values than their corresponding previous ones).
Consequently, here is the corresponding Form_BeforeUpdate method (note
that c is declared as Long and not as Variant because both Judet from LOCA-
LITATI and Tara from JUDETE are totally defined (do not accept nulls);
also note that, for any cancelable event, your errors processing section
52
Formally, (hHARBORS)(nNEIGHBORSOC)(Ocean( h) = Ocean(n) Country(n) =
Country State City(h))
320
should generally also include a Cancel = True statement, for canceling cor-
responding event in case of errors):
'***********************************************
Private Sub Form_BeforeUpdate(Cancel As Integer)
'***********************************************
'enforces the OceanHarborsConstraint: In order for a country
'to have an oceanic harbor, it has to be neighbor to the
'corresponding ocean.
Dim c As Long
On Error GoTo err_point
If Not Me.NewRecord And Me![#PO] = Me![#PO].OldValue And _
Me!Ocean = Me!Ocean.OldValue Then
GoTo exit_sub
End If
c = DLookup("Tara", "JUDETE", "[#J]=" & _
DLookup("Judet", "LOCALITATI", "[#L]=" & Me![#PO]))
If IsNull(DLookup("[#VM]", "VECINATATIOT", "TaraVecina=" & _
c & " AND Ocean=" & Me!Ocean)) Then
If vbCancel = MsgBox("Would you like to add neighborhood" _
& " data for this ocean and corresponding country?", _
vbQuestion + vbOKCancel + vbDefaultButton2, _
"Corresponding country is not neighbor with this " _
& "ocean...") Then
Cancel = True
MsgBox "Please change either the harbor or the ocean!", _
vbCritical, "Impossible to save this pair..."
Else
DoCmd.RunSQL "INSERT INTO VECINATATIOT(TaraVecina, " _
& "Ocean) VALUES (" & c & ", " & Me!Ocean & ")"
MsgBox "Succesfully added corresponding neighborhood " _
& "data!", vbInformation, "This pair will be saved..."
End If
End If
exit_sub: Exit Sub
err_point: MsgBox Err.Source & "-->" & Err.Description, _
vbExclamation, "Error in class Form_PorturiOceanice " _
& ". method Form_BeforeUpdate..."
Cancel = True
End Sub
321
10.3 Prerequisites in Oracle: Triggers
10.4 Exercises in Oracle
10.5 Best practice rules
1. Whenever possible, enforce constraints preventively rather than by
curing.
2. Always enforce generalized commutativity constraints by automati-
cally adding missing rows (at most asking for users’ confirmations), rather
than by rejecting violating updates/inserts.
10.6 Homework H10.1 Design and develop, in any high-level programming language or in
any extended SQL of your choice (preferably embedding SQL) necessary
methods / triggers for enforcing all remaining non-relational constraints from
lab 9 exercises, as well as those discovered in H9.1 above.
322
Chapter 11. 11th Lab: Reporting in Access & 2nd DB test
training
11.1 Reporting in Access
11.1.1 Prerequisites
Access also provides a Report Generator and Manager (ARGM). Reports are
very similar to forms, except that they are designed for outputting data, in-
stead of inputting and managing it and prepared for printing, rather than for
displaying (although reports may also be previewed before or even instead of
printing); their main enhacement is (nested) grouping of data, with associa-
ted totals (for cardinalities, sums, averages, etc.). Access reporting facilities
are not that powerful as, for example, Crystal Reports’ ones, but they are
comparable with MS Reporting Services for SQL Server ones.
Although ARGM wizard is also able to help with the subjacent query design
too, it is a best practice to first design, test, and save independently its sub-
jacent query and give to ARGM its name; then you can embed it in the report
(for easing project management), by replacing the query name with its body.
Just like for forms, you can design reports either from scratch or using the
Report Wizard (ARW). To create a report from scratch, click on the Report
Design or Blank Report icons of the Create tab of the Ribbon. If you click on
the Report one, a datasheet-type report is automatically created for the cur-
rently selected table (see figure 11.1 for an example); then, you can use the
Report Layout Tools (ARLT) for enhancing your report. Note that for printing
labels (a particular type of reports), there is a specialized Label Wizard that
you can launch by clicking on the Labels icon of the Create tab of the Rib-
bon (see figure 11.2).
Generally, the most productive way to produce any other types of reports is
first using ARW and then enhancing it with ARLT. To start ARW, click on
the Report Wizard icon of the Create tab of the Ribbon.
Figure 11.3 shows its first window, where you may choose the desired subja-
cent query name or start designing one, by choosing desired tables and co-
lumns. When clicking on Next, a second window is displayed (see figure
11.4) for adding (up to four levels of) grouping, by clicking on the > button
323
(see figure 11.5); you can remove groups by clicking on <; you can also
change groups hierarchy by clicking on the Priority’s up and down buttons.
Figure 11.1 An example of a datasheet-type report generated by ARGM
Figure 11.2 The first screen of the Label Wizard
324
Figure 11.3 ARW’s first screen: chosing/designing the subjacent query
Figure 11.4 ARW’s second screen: specifying groups
325
Figure 11.5 ARW’s second screen: specifying groups hierarchy
By clicking on the Grouping Options button, you can specify grouping (for
the current group) either normally (i.e. on whole corresponding column va-
lues) or by using only a prefix of the corresponding column values (made out
of 1, 2, …, or 5 characters, see figure 11.6); you should click on either OK or
Cancel in order to close this modal window and get back to the ARW’s se-
cond screen.
When you finished grouping, click on Next for advancing to the ARW’s third
screen, where you can define (up to four levels of) sorting (see figure 11.7).
By clicking on the Summary Options button, you can add (groups and over-
all) summaries on any report’s numeric column (see figure 11.8): you can al-
so choose whether you would like to print the detail and summary or only
summary corresponding values, as well as including or not percent of totals
for sums; click on OK or Cancel in order to close this modal window and get
back to the ARW’s third screen.
When you finished sorting, click on Next for advancing to the ARW’s fourth
screen, where you can define both layout and orientation (see figure 11.9).
For wide reports, choose Landscape instead of the default Portrait orienta-
326
tion. Depending on your customers’ or/and personal’s taste, choose one of
the three available layouts: Stepped, Block, or Outline (note that the left icon
changes accordingly in order to help you choosing).
When you finished specifying layout and orientation, click on Next for ad-
vancing to the ARW’s fifth screen, where you can change the default report
name suggested automatically (the name of the subjacent query, for exam-
ple) and save and preview or continue with manually modifying its design
(see figure 11.10).
After specifying the desired report name, click on Finish, with the default
option Preview, for inspecting the generated report (see figure 11.11 for its
first page and 11.12 for its last one). By using the footnote bar Page you can
inspect all of the corresponding report pages.
Generally, for reports having many columns, due to the limitations imposed
by paper sizes, the effect of the default option Adjust the field width so all
fields fit on page from ARW’s fourth screen is that some field sizes are too
small for the corresponding column values, so that ‘#’’s are displayed in-
stead. Consequently, manual design adjustments are necessary.
Figure 11.6 ARW’s second screen: specifying grouping options
327
Figure 11.7 ARW’s third screen: specifying sorting options
Figure 11.8 ARW’s third screen: specifying summary options
328
Figure 11.9 ARW’s fourth screen: specifying layout and orientation
Figure 11.10 ARW’s fifth screen: specifying report title
329
Figure 11.11 An example of ARW automatically generated report first page
Figure 11.12 An example of ARW automatically generated report last page
330
In order to modify a report design, just like for forms, open it in Design view:
Figure 11.13 An example of a report Design View
Besides pure graphics (like centering the report’s title, changing fonts, sizes,
colors, etc.), you can also modify everything else, from the source data to
grouping, from sorting to layout, etc. For example, in order to ease db mana-
gement, you can replace the subjacent query name by its body: open the que-
ry in SQL design mode (see chapters 2 to 6 above), select and copy its body,
and then, just like for forms, right-click the upper-left corner of the report de-
sign view, click on Report Properties, then on the Data tab, paste in the Re-
cord Source property the query’s body (see figure 11.14), and, finally, close
the Properties window and save the report design.53
Figure 11.14 Replacing the name of the subjacent query with its body
53
Unfortunately, I just discovered that Access 2010 behaves unexpectedly after such a re-
placement: it replaces the City names with the corresponding country Capital ones!! More-
over, even if you delete Capital and replace it with City, when saving Access replaces again
City with Capital!!! Moreover, calculated cardinals are no more correctly computed either!!!
331
Next, attach a method to the No Data event, for not printing the report when
its data source is empty: just like for forms, re-open the Report Properties,
click on the Event tab, choose Event Procedure from the On No Data com-
bo-box (see figure 11.15), click on the “…” button, and then enter the corres-
ponding VBA code into the automatically generated method signature (see fi-
gure 11.16); save, compile the project, and then get back to the design view
for closing the Properties window.
Figure 11.15 Attaching an event procedure to the No Data report event
Figure 11.16 Cancelling printing of empty reports
Finally, we have to take care of all data which is not displayed correctly, be-
cause of the lack of space; first, note (see figure 11.3 above) that all data is
displayed on only one (detail) row, with the corresponding header displayed
on each page, whilest both country and state group headers are empty. This
is why, for example, country and state names (but also corresponding popu-
332
lations, capitals, etc.) are repeatedely printed for each city (see figures 11.11
and 11.12).
For factoring out all country data, first enlarge the Country Header and then
drag and drop all labels and corresponding data (in this particular case, name,
code, population, states’ designation, and capital) into it (see figure 11.17).
Save your work and switch to the preview mode to check that all of your si-
zes and alignements are ok (see figure 1.18).
Then, similarly, factorize state data (in this particular case, name, code, po-
pulation, and capital - see figure 11.19), save, and check it too (see figure
1.20).
Next, re-arranging detail data will allow it too to be correctly printed (see fi-
gures 11.21 and 11.22). Finally, re-arranging totals completes the manual ad-
justments of the report (see figures 11.23 and 11.24).
Figure 11.17 Factorizing outer group data
333
Figure 11.18 Checking factorizing outer group data
Figure 11.19 Factorizing inner group data
334
Figure 11.20 Checking factorizing inner group data
Figure 11.21 Arranging detail data
335
Figure 11.22 Checking re-arranged detail data
Figure 11.23 Arranging sums
336
Figure 11.24 Checking re-arranged sums
Note that you can also check/modify grouping and sorting (see figure 11.25),
totals, tab order, page numbers, logos, titles, date and times, subreports, etc.
For both grouping and sorting, you can add printing properties by clicking on
the corresponding More button: you can the grouping prefix length to any
natural number, add/remove groups titles, headers, and/or footers, and, espe-
cially, add/remove page breaks either before any new corresponding group
values or such that any new group value header is followed on the same page
by at least the first row of it (see figure 11.26).
You may also use all other available report and control events (see figure
11.27), just like for forms, for canceling opening or closing, dynamically
changing/filtering data sources, passing parameters, changing colors/fonts/si-
zes dynamically, on a row or cell basis, etc.
337
Figure 11.25 Checking and/or modifying grouping and sorting
Figure 11.26 Adding grouping and/or sorting printing properties
338
Figure 11.27 Available report events
11.1.2 Exercises
P11.1 Design and implement for the lab db a report for printing country
names, codes, populations, state designations, and capitals (on an outer
group, ascendingly on names), state names, codes, capitals, and population
(on an inner group, ascendingly on names), and city names and populations
(in the descending order of population and then ascending on name), with to-
tals of number of cities and sum of their population per state and country, of
number of states and sum of their population per country, as well as grand
totals of all of this data.
Solution:
Obviously, needed data is stored in tables COUNTRIES, STATES, and CI-
TIES (see figures 2.7, 2.2, and 2.4 respectively); note that three instances of
CITIES are necessary, one for the cities themselves, a second one for the
country capitals, and a third one for the state capitals; also note that both
country and state capitals are not required, so outer joins are needed for this
two latter instances in order not eliminate countries and/or states for which
corresponding capitals are not known.
Consequently, here is the needed subjacent query (saved it as ReportQ):
339
SELECT COUNTRIES.Country, COUNTRIES.CountryCode,
COUNTRIES.Population, COUNTRIES.StateDesignation,
CAPITALS.City AS Capital, STATES.State,
STATES.StateCode, STATES.Population AS StatePopulation,
STATE_CAPITALS.City AS StateCapital, CITIES.City,
CITIES.Population AS CityPopulation
FROM (COUNTRIES LEFT JOIN CITIES AS CAPITALS
ON COUNTRIES.Capital = CAPITALS.x) INNER JOIN ((STATES
LEFT JOIN CITIES AS STATE_CAPITALS
ON STATES.StateCapital = STATE_CAPITALS.x) INNER JOIN
CITIES ON STATES.x = CITIES.State)
ON COUNTRIES.x = STATES.Country;
Then, by following exactly the steps from sub-section 11.1.1, the requested
report is obtained, whose output is (partially) presented in figure 11.24.
P11.2 Design and implement for the GeografieDB db a report for printing
oceans, continents, and archipels names, all of them being (normal) groups,
in this order, islands ant their main countries names, as the fourth group, and
countries names and island area sizes that they occupy, in descending order
of the occupied area size and then ascending on country name, with totals
(both absolute and percentage) of both island and countries occupied areas
on all groups, as well as grand totals of all of this data.
Solution:
Needed data is stored in the following 4 tables: INSULE (ISLANDS, figure
11.28), CONTINENTE (CONTINENTS, figure 11.29), TARI (COUNTRIES,
figure 11.30), ARHIPELAGURI (ARCHIPELS, figure 11.31), and Ocupare-
Insule (IslandsOccupants, figure 11.32). First of all please note that the area
occupied by the main country on an island is not stored, as it is computable
from the island area and the sum of the areas occupied by all other countries
that are co-occupying it (if any); consequently, these values need to be com-
puted too. The following query (TotIslandsOccupiedAreas) is computing the
total areas occupied by the countries in OcupareInsule (IslandsOccupants),
per island:
SELECT Insula, Sum(Suprafata) AS TotOccupiedArea
FROM OcupareInsule
GROUP BY Insula;
Then, based on these values and on the total island area, the following query
(MainIslandsCountriesAreas) computes areas occupied by main countries:
340
SELECT [#I], Tara,
IIf(IsNull([TotOccupiedArea]),[Suprafata],[Suprafata]-
[TotOccupiedArea]) AS MainCountryArea
FROM TotIslandsOccupiedAreas RIGHT JOIN INSULE
ON TotIslandsOccupiedAreas.Insula = INSULE.[#I];
As for the “core” report query, note that two instances of CONTINENTE are
needed (one for the ocean and the other for the continent to which islands be-
long); similarly, two instances of TARI are also needed (one for the main
country and the other for the rest of other islands’ occupants); also note that,
as not every island belongs to an archipel, Arhipelag from INSULE accepts
nulls, so that an outer join is needed in order not to eliminate such islands; si-
milarly, as not every island is shared among several countries, another outer
join is needed between INSULE and OcupareInsule, in order not to eliminate
islands that are occupied by only one country. Consequently, this core query
(save it as IslandsWithTheirCountries0) has the following SQL expression
(see figure 11.33 for its QBE/design view one):
SELECT [#I], OCEANS.Continent AS Ocean, CONTINENTE.Continent,
ARHIPELAGURI.Archipel, INSULE.Island, INSULE.Suprafata
AS IslandArea, MainCountries.Country AS MainCountry,
TARI.Country, OcupareInsule.Suprafata AS CountryArea
FROM (((((INSULE LEFT JOIN OcupareInsule
ON INSULE.[#I] = OcupareInsule.Insula) INNER JOIN
CONTINENTE ON INSULE.Continent = CONTINENTE.[#C])
LEFT JOIN ARHIPELAGURI
ON INSULE.Arhipelag = ARHIPELAGURI.[#A]) INNER JOIN
TARI AS MainCountries
ON INSULE.Tara = MainCountries.[#T]) LEFT JOIN TARI
ON OcupareInsule.Tara = TARI.[#T]) INNER JOIN
CONTINENTE AS OCEANS ON INSULE.Ocean = OCEANS.[#C];
Finally, we have to also add areas occupied by main countries (computed by
the MainIslandsCountriesAreas query above; save the result as IslandsWith-
TheirCountries):
SELECT IslandsWithTheirCountries0.*, MainCountryArea
FROM IslandsWithTheirCountries0 INNER JOIN
MainIslandsCountriesAreas
ON IslandsWithTheirCountries0.[#I] =
MainIslandsCountriesAreas.[#I];
By following exactly the steps from sub-section 11.1.1, the requested report
is obtained, whose first and last design view screens are presented in figures
11.34 and 11.35, while the corresponding running ones in 11.36 and 11.37.
341
Figure 11.28 The GeografieBD INSULE (ISLANDS) table
Figure 11.29 The GeografieBD CONTINENTE (CONTINENTS) table
342
Figure 11.30 The GeografieBD TARI (COUNTRIES) table
Figure 11.31 The GeografieBD ARHIPELAGURI (ARCHIPELS) table
343
Figure 11.32 The GeografieBD OcupareInsule (IslandsOccupants) table
Figure 11.33 The IslandsWithTheirCountries0 core report query design view
344
Figure 11.34 The IslandsWithTheirCountries report’s first design view
screen
Figure 11.35 The IslandsWithTheirCountries report’s last design view screen
345
Figure 11.36 The IslandsWithTheirCountries report’s first screen
Figure 11.37 The IslandsWithTheirCountries report’s last screen
346
11.2 2nd DB test training Consider the files (managed by file management subsystems for operating
systems like Windows, Unix, Linux, etc.) of any disk partition, for which are
of interest (in this simplified subuniverse) only name, extension, folder, logic
drive (identified by an ASCII capital letter), start (storing) address, size, and
whether or not they are folders. Hint: recall that, by convention, the name of
root folders (and only of root folders) is “\” and that they do not have exten-
sions. Design the db fragment needed for storing this data and implement it
on the platform of your choice; also add at least two plausible rows per table.
Solution:
Folders are zero-length (meta-)files, stored in the file management system’s
tables, structured, for such operating systems, as trees (one per logic drive);
any file belongs to a folder, except for the root folders, which do not belong
to anything; any file belongs to a logic drive and any formatted logic drive
has only one root folder (wheres there’s none for unformatted ones).
Consequently, the corresponding E-RD and restrictions list are the following
(8 + 14 = 22 points):
Name Ext StartAddress Size Folder?
Folder FILES
LogicDrive RootFolder
LOGIC_DRIVES LogicDriveLetter
FILES (The set of all existing files, including folders)
Max. cardinality: 1024
(for example, Win 2012 Server accepts HDDs of
18EB each; with VHDX Hyper-V, it has much more than that) (R0)
Ranges:
- Name and Ext: ASCII(255) (R1)
- StartAddress and Size: [0, …, 1024
-1] (R2)
- Folder?: {True, False} (R3)
Mandatory: Name, Size, Folder? (R4)
Uniqueness: there may not be two files having same name and
same extension in a same folder (R5)
347
Other restrictions:
- all files having Size > 0 should have a not null start address (R6)
- any folder has Size = 0 (R7)
- any file having Size = 0 has no start address (R8)
LOGIC_DRIVES (The set of all existing logic drives)
Max. cardinality: 27 (R9)
Ranges: LogicDriveLetter: [‘A’, …, ‘Z’] (R10)
Mandatory: LogicDriveLetter (R11)
Uniqueness: LogicDriveLetter (R12)
Other restrictions:
- any file belongs to a logic drive (R13)
- unformatted logic drives do not have root folders; formatted
ones do have one (R14)
- all files, except for root folders, belong to a folder (R15)
- any root folder has name “\” and no extension (R16)
The result of applying the translation algorithm from E-RDs and restriction
lists to mathematical schemes is the following (15 points):
FILES
x NAT(24), total (according to R0)
Name ASCII(255), total (according to R1 and R4)
Ext ASCII(255) (according to R1)
StartAddress [0, …, 1024
-1] (according to R2)
Size [0, …, 1024
-1], total (according to R2 and R4)
Folder? BOOLE, total (according to R3 and R4)
Folder : FILES FILES (according to R15)
LOGIC_DRIVES
x NAT(2), total (according to R9)
LogicDriveLetter [‘A’, …, ‘Z’], total (according to R10, R11, and R12)
LogicDrive : FILES LOGIC_DRIVES, total (according to R13)
RootFolder : LOGIC_DRIVES FILES (according to R14)
C5: Name Ext Folder LogicDrive key (according to R5)
C6: (xFILES)(Size(x) > 0 StartAddress(x) NULLS)
(according to R6)
C7: (xFILES)(Folder?(x) Size(x) = 0) (according to R7)
C8: (xFILES)(Size(x) = 0 StartAddress(x) NULLS)
(according to R8)
348
C15: (xFILES)(Name(x) = “\” Ext(x) NULLS
Folder(x) NULLS) (according to R15)
C16: (xFILES)(yLOGIC_DRIVES)
((Folder(x) NULLS Name(x) = “\” Ext(x) NULLS)
(x = RootFolder(y) Folder(x) NULLS)) (according to R16)
Results of applying the first refinement algorithm (for assisting sets, func-
tions, and constraints design) are the following (40 points):
a. Sets: both FILES and LOGIC_DRIVES are atomic type object sets not
semantically overloaded.
b. Functions:
- Name is well defined as any file has a name and only one name;
one-to-oneness: there may be several files having same name even in
a same folder (provided they have different extensions);
ontoness: it is not mandatory that all possible ASCII strings of at
most 255 characters be file names on a same partition (moreover,
there are even special characters not allowed in file names);
- Ext is well defined as any file may also have an extension and only
one extension;
one-to-oneness: there may be several files having same extension e-
ven in a same folder (provided they have different names);
ontoness: it is not mandatory that all possible ASCII strings of at
most 255 characters be file extensions on a same partition (more-
over, there are even special characters not allowed in file exten-
sions);
- LogicDrive is well defined as any file belongs to a logic drive and on-
ly one logic drive;
one-to-oneness: there may be several files belonging to a same logic
drive;
ontoness: it is not mandatory that all possible ASCII capital letters be
assigned to logic drives on a same partition;
- StartAddress is well defined as any file having Size > 0 is stored on a
physical drive starting from an address and only one address;
one-to-oneness: there may not be several files starting from a same
address in a same partition (in fact, not even on a same physical
drive, regardless of partitions) StartAddress is one-to-one;
349
ontoness: it is not mandatory that all possible integers between 0
and1024
-1 be file start addresses on a same partition;
- Size is well defined as any file has a size and only one size;
one-to-oneness: there may be several files having a same size on any
partition;
ontoness: it is not mandatory that all possible integers between 0
and1024
-1 be file sizes on a same partition;
- Folder? is well defined as any file is either a folder or an ordinary
file;
one-to-oneness: there may be several ordinary as well as folder files
on any partition;
ontoness: all files may be empty folders in any partition;
- Folder is well defined as any file, except for root folders, belongs to a
folder and only one folder;
one-to-oneness: there may be several files belonging to a same folder
on any partition;
ontoness: there may be empty folders in any partition;
acyclicity: no folder may belong to itself, either directly or indirectly
(generally, any tree is free of cycles) Folder acyclic should be ad-
ded to the db scheme;
- LogicDriveLetter is well defined as any logic drive has an operating
system assigned letter and only one letter;
one-to-oneness: there may not be several logic drives having assigned
a same letter on any partition;
ontoness: it is not mandatory that all partitions have always 27 logic
drives each;
- LogicDrive is well defined as any file belongs to a logic drive and
only to one logic drive;
one-to-oneness: there may not be several logic drives having assigned
a same letter on any partition;
ontoness: it is not mandatory that all logic drives of a partition be
always formatted;
- RootFolder is well defined as any formatted logic drive has one root
folder and only one root folder;
one-to-oneness: no folder may be the root folder of several logic
drives (regardless of partitioning) RootFolder is one-to-one;
350
ontoness: it is not mandatory that all files be root folders.
c. Constraints:
- trivially, files only belong to folders, not to ordinary files the con-
straint (xFILES)(Folder?(Folder(x))) should be added to the db
scheme;
- unfortunately, C5 only covers files having not-null extensions: addi-
tionally, there may not be two files in a folder having same names
and no extensions C5 has to be replaced by the following stronger
constraint (which is implying R5):
(x,yFILES)((Ext(x) NULLS Ext(y) NULLS) Name
Ext Folder? LogicDrive(x) Name Ext Folder?
LogicDrive(y)) (Ext(x) NULLS Ext(y) NULLS) Name
Folder? LogicDrive(x) Name Folder? LogicDrive(y)));
- trivially, any root folder should be a folder, but the corresponding
constraint ((xLOGIC_ DRIVES)(Folder?(RootFolder(x)))) is im-
plied by C16;
- trivially too, any file should belong to a folder from a same logic
drive the constraint (xFILES)(LogicDrive(Folder(x)) = Logic-
Drive(x)) should be added to the db scheme too;
- trivially too, no file may be stored on a logic drive, unless that drive
is formatted (i.e. it has a root folder) the constraint (xFILES)
((Name(x) “\” Ext(x) NULLS) RootFolder(LogicDrive(x)
NULLS) should be added to the db scheme too.
Results of applying the second refinement algorithm (for assisting keys
discovery) are the following (7 points):
FILES:
n = 6 (as StartAddress is a key, only Name, Ext, Size, Folder?, Folder,
and LogicDrive remain to be considered), K = {StartAddress, Name Ext
Folder LogicDrive};
non-primeness:
- Name, Ext, Folder, and LogicDrive are prime, as they already consti-
tute a key;
351
- Size is trivially non-prime: file size cannot participate in unique files
identification;
- Folder? is trivially non-prime: file type (ordinary or folder) cannot
participate in unique files identification.
Consequently, n’ = 4 (only Name, Ext, Folder, and LogicDrive remain to be
considered); as all of them are making up a key, no subproduct may be a key,
so there is nothing to do!.
In conclusion, FILES have no other keys than those in K = {StartAddress,
Name Ext Folder LogicDrive}.
LOGIC_DRIVES:
K = {LogicDriveLetter, RootFolder}, n = 0 (as LogicDriveLetter and Root-
Folder are both keys) nothing to do.
Results of applying the third refinement algorithm (for assisting E-RD cycles
analysis) are the following (3 points):
There are two cycles: Folder, which has been analyzed already in the first re-
finement step above, and the uni-directional binary one made out of Logic-
Drive : FILES LOGIC_DRIVES and RootFolder : LOGIC_DRIVES
FILES.
- RootFolder LogicDrive =? 1FILES (should any file be the root folder
of the logic drive to which it belongs?) – trivially no, as not all files
are folders (and only one folder per logic drive is a root one)
- LogicDrive RootFolder =? 1LOGIC_DRIVES (should any root folder of a
logic drive be a file of that logic drive?) – trivially yes LogicDrive
RootFolder = 1LOGIC_DRIVES has to be added too.
352
To conclude with, here is the final mathematical scheme obtained (28
points):
FILES
x NAT(24), total
Name ASCII(255), total
Ext ASCII(255)
StartAddress [0, …, 1024
-1]
Size [0, …, 1024
-1], total
Folder? BOOLE, total
Folder : FILES FILES, acyclic
C6: (xFILES)(Size(x) > 0 StartAddress(x) NULLS)
C7: (xFILES)(Folder?(x) Size(x) = 0)
C8: (xFILES)(Size(x) = 0 StartAddress(x) NULLS)
C17: (xFILES)(Folder?(Folder(x)))
LOGIC_DRIVES
x NAT(2), total
LogicDriveLetter [‘A’, …, ‘Z’], total
LogicDrive : FILES LOGIC_DRIVES, total
RootFolder : LOGIC_DRIVES FILES
C5: (x,y FILES)((Ext(x) NULLS Ext(y) NULLS) Name Ext
Folder LogicDrive(x) Name Ext Folder LogicDrive(y))
(Ext(x) NULLS Ext(y) NULLS)
Name Folder LogicDrive(x) Name Folder LogicDrive(y)))
C15: (xFILES)(Name(x) = “\” Ext(x) NULLS
Folder(x) NULLS)
C16: (xFILES)(yLOGIC_DRIVES)
((Folder(x) NULLS Name(x) = “\” Ext(x) NULLS)
(x = RootFolder(y) Folder(x) NULLS))
C18: (xFILES)(LogicDrive(Folder(x)) = LogicDrive(x))
C19: (xFILES) ((Name(x) “\” Ext(x) NULLS)
RootFolder(LogicDrive(x) NULLS)
C20: LogicDrive RootFolder = 1LOGIC_DRIVES
Applying the algorithm for translating mathematical schemes into relational
ones (24 points) and non-relational constraint lists (12 points) yields the
following:
353
FILES(x, StartAddress, Name Ext Folder LogicDrive)
x Name Ext Fol-
der
Fol-
der?
Size StartAd-
dress
LogicDrive
au-
ton.
(24)
ASCII
(255)
ASCII
(255)
Im
(x)
BO-
OLE
[0, …,
1024
-
1]
[0, …,
1024
-1]
Im(LOGIC
_DRIVES.x)
NO
T
NU
LL
NOT
NULL
NOT
NUL
L
NOT
NUL
L
NOT NULL
1 \ T 0 1
2 boot ini 1 F 35 128 1
LOGIC_DRIVES(x, LogicDriveLetter, RootFolder)
x LogicDriveLetter RootFolder
auton.(2) [‘A’, …, ‘Z’] Im(FILES.x)
NOT NULL NOT NULL
1 C 1
2 D
Non-relational constraints are:
C5’: (x,y FILES) ((Ext(x) NULLS Ext(y) NULLS)
Name Folder LogicDrive(x) Name Folder LogicDrive(y)))
(note that C5 C5’ Name Ext Folder LogicDrive key)
C6: (xFILES)(Size(x) > 0 StartAddress(x) NULLS)
C7: (xFILES)(Folder?(x) Size(x) = 0)
C8: (xFILES)(Size(x) = 0 StartAddress(x) NULLS)
C15: (xFILES)(Name(x) = “\” Ext(x) NULLS
Folder(x) NULLS)
C16: (xFILES)(yLOGIC_DRIVES)
((Folder(x) NULLS Name(x) = “\” Ext(x) NULLS)
(x = RootFolder(y) Folder(x) NULLS))
C17: (xFILES)(Folder?(Folder(x)))
C18: (xFILES)(LogicDrive(Folder(x)) = LogicDrive(x))
C19: (xFILES)((Name(x) “\” Ext(x) NULLS)
RootFolder(LogicDrive(x) NULLS)
C20: LogicDrive RootFolder = 1LOGIC_DRIVES
C21: Folder : FILES FILES, acyclic
354
11.2.1 Access implementation
Applying the algorithm for translating relational schemes into Access dbs,
the following three DDL SQL statements are needed (20 points):
createFILES:
CREATE TABLE FILES (x COUNTER PRIMARY KEY, Name
VARCHAR(255) NOT NULL, Ext VARCHAR(255), Folder
Long, CONSTRAINT fkFolder FOREIGN KEY (Folder)
REFERENCES FILES, Size Double NOT NULL, [Folder?]
BIT NOT NULL, StartAddress Double UNIQUE,
LogicDrive Long NOT NULL, CONSTRAINT skFILES UNIQUE
(Name, Ext, Folder, LogicDrive));
createLOGIC_DRIVES:
CREATE TABLE LOGIC_DRIVES (x COUNTER PRIMARY KEY,
LogicDriveLetter CHAR(1) NOT NULL UNIQUE,
RootFolder Long UNIQUE, CONSTRAINT fkRootFolder
FOREIGN KEY (RootFolder) REFERENCES FILES);
alterFILES:
ALTER TABLE FILES ADD CONSTRAINT fkLogicDrive
FOREIGN KEY (LogicDrive) REFERENCES LOGIC_DRIVES;
Through Access’ GUI, the following has to to be added to this db (29
points):
- For all text columns (Name, Ext, and LogicDriveLetter) correspond-
ing Allow zero length property should be turned to No (for rejecting
dirty nulls).
- For all numeric ones (Size, StartAddress), as well as text ones that
have as (co-)domains subsets of Access’ data types (LogicDriveLet-
ter), their Validation Rule and Text properties should be filled accor-
dingly:
LogicDriveLetter: Between “A” and “Z” (“Valid logic
drive letters are between A and Z!”) (alternatively, and even
better, you might turn this text box into a combo one and
attach to it a value list containing all capital letters between
“A” and “Z”);
355
Size and StartAddress: Between 0 and 10^24-1 (“Va-
lid file size/start address values are between 0 and 10^24 –
1!”);
- For all columns for which default values might help insertions, the
corresponding Default properties should be added too:
Size: 0
Folder?: False
- For all foreign keys (Folder, LogicDrive, RootFolder), corresponding
text boxes should be replaced by combo ones, for hiding pointers and
displaying instead corresponding semantic key values:
Folder: SELECT FILES.x, [LogicDriveLetter] & ":\..." &
IIf(IsNull([FILES].[Folder]),"\",[FOLDERS].[Name] &
IIf(IsNull([FOLDERS].[Ext]),"","." &
[FOLDERS].[Ext]) & "\") & [FILES].[Name] &
IIf(IsNull([FILES].[Ext]),"","." & [FILES].[Ext])
AS Path
FROM LOGIC_DRIVES INNER JOIN (FILES LEFT JOIN FILES
AS FOLDERS ON FILES.Folder = FOLDERS.x)
ON LOGIC_DRIVES.x = FILES.LogicDrive
WHERE FILES.[Folder?]
ORDER BY [LogicDriveLetter] & ":\..." &
IIf(IsNull([FILES].[Folder]),"\",[FOLDERS].[Name] &
IIf(IsNull([FOLDERS].[Ext]),"","." &
[FOLDERS].[Ext]) & "\") & [FILES].[Name] &
IIf(IsNull([FILES].[Ext]),"","." & [FILES].[Ext]);
LogicDrive: SELECT x, LogicDriveLetter FROM LOGIC_DRIVES
ORDER BY LogicDriveLetter;
RootFolder: SELECT FILES.x, [LogicDriveLetter] & ":\..." &
IIf(IsNull([FILES].[Folder]),"\",[FOLDERS].[Name] &
IIf(IsNull([FOLDERS].[Ext]),"","." &
[FOLDERS].[Ext]) & "\") & [FILES].[Name] &
IIf(IsNull([FILES].[Ext]),"","." & [FILES].[Ext])
AS Path
FROM LOGIC_DRIVES INNER JOIN (FILES LEFT JOIN FILES
AS FOLDERS ON FILES.Folder = FOLDERS.x)
ON LOGIC_DRIVES.x = FILES.LogicDrive
WHERE FILES.[Folder?] AND IsNull(FILES.Folder)
ORDER BY [LogicDriveLetter] & ":\..." &
IIf(IsNull([FILES].[Folder]),"\",[FOLDERS].[Name] &
IIf(IsNull([FOLDERS].[Ext]),"","." &
[FOLDERS].[Ext]) & "\") & [FILES].[Name] &
IIf(IsNull([FILES].[Ext]),"","." & [FILES].[Ext]);
356
For populating the two tables with their demos rows the following five SQL
DML statements are needed (3 points):
INSERT INTO LOGIC_DRIVES(LogicDriveLetter) VALUES ("C");
INSERT INTO LOGIC_DRIVES(LogicDriveLetter) VALUES ("D");
INSERT INTO FILES(Name, Size, [Folder?], LogicDrive)
VALUES ("\", 0, True, 1);
INSERT INTO FILES(Name, Ext, Folder, Size, StartAddress,
LogicDrive) VALUES ("boot", "ini", 1, 35, 128, 1);
UPDATE LOGIC_DRIVES SET RootFolder = 1 WHERE x = 1;
According to the algorithm for assisting enforcement of non-relational con-
straints in Access, in order to enforce the above non-relational constraints we
first have to create (by using the Form Wizard) the two corresponding data-
sheet forms LOGIC_ DRIVES and FILES (and preferably embed the latter as
a subform of the former, linked on LOGIC_DRIVES.x = FILES.LogicFile).54
- C5’: (x,y FILES) ((Ext(x) NULLS Ext(y) NULLS)
Name Folder LogicDrive(x) Name Folder LogicDrive(y)))
Obviously, it has to be enforced in class Form_FILES; as three of its co-
lumns are involved, the needed event is Form_BeforeUpdate (14 points):
'***********************************************
Private Sub Form_BeforeUpdate(Cancel As Integer)
'***********************************************
On Error GoTo err_point
'enforces constraint C5'
If IsNull(Me!Ext) Then
If Not IsNull(Me!Folder) Then
If Not IsNull(DLookup("x", "FILES", "Name='" & Me!Name _
& "' AND Ext Is Null AND Folder=" & Me!Folder & _
" AND LogicDrive=" & Me!LogicDrive & " AND x<>" & _
Me!x)) Then
Cancel = True
End If
Else
If Not IsNull(DLookup("x", "FILES", "Name='" & Me!Name _
& "' AND Ext Is Null AND Folder Is Null AND x<>" _
& Me!x & " AND LogicDrive=" & Me!LogicDrive)) Then
Cancel = True
End If
End If
If Cancel Then
MsgBox "Please change either Name, Ext, or Folder!", _
vbCritical, "There exists a file with same name " _
54
2 points
357
& "and no extension in this folder..."
End If
End If
Exit Sub
err_point: MsgBox Err.Source & "->" & Err.Description, _
vbCritical, _
"Error in method Form_FILES.Form_BeforeUpdate..."
Cancel = True
End Sub
- C6: (xFILES)(Size(x) > 0 StartAddress(x) NULLS)
Obviously, it has to be enforced in class Form_FILES; as two of its columns
are involved, the needed event is Form_BeforeUpdate too, so the following
code should be added to the above method before Exit Sub (5 points):
'enforces constraint C6
If Me!Size > 0 And IsNull(Me!StartAddress) Then
Cancel = True
MsgBox "Please either change Size or specify the " & _
"StartAddress!", vbCritical, _
"StartAddress is compulsory when Size > 0..."
End If
- C7: (xFILES)(Folder?(x) Size(x) = 0)
Obviously, it has to be enforced in class Form_FILES; as two of its columns
are involved, the needed event is Form_BeforeUpdate too, so the following
code should be added to the above method before Exit Sub (5 points):
'enforces constraint C7
If Me![Folder?] And Me!Size <> 0 Then
Cancel = True
MsgBox "Please change Size to 0!", vbCritical, _
"Folders should have Size = 0..."
End If
- C8: (xFILES)(Size(x) = 0 StartAddress(x) NULLS)
Obviously, it has to be enforced in class Form_FILES; as two of its columns
are involved, the needed event is Form_BeforeUpdate too, so the following
code should be added to the above method before Exit Sub (5 points):
'enforces constraint C8
If Me!Size = 0 And Not IsNull(Me!StartAddress) Then
Cancel = True
MsgBox " Please either change Size or delete the " & _
"StartAddress!", vbCritical, _
"When Size = 0 there should not be StartAddress..."
End If
- C15: (xFILES)(Name(x) = “\” Ext(x) NULLS
Folder(x) NULLS)
358
Obviously, it has to be enforced in class Form_FILES; as three columns are
involved, the needed event is Form_BeforeUpdate, so the following code
should be added to the above method before Exit Sub (5 points):
'enforces constraint C15
If IsNull(Me!Folder) And (Me!Name<>"\" Or Not IsNull(Ext)) _
Then
Cancel = True
MsgBox "Please specify the folder to which this file" _
& " belongs!", vbCritical, "Only root folders do " _
& "not belong to any folder..."
End If
- C16: (xFILES)(yLOGIC_DRIVES)
((Folder(x) NULLS Name(x) = “\” Ext(x) NULLS)
(x = RootFolder(y) Folder(x) NULLS))
Obviously, the first half ((xFILES) (Folder(x) NULLS Name(x) =
“\” Ext(x) NULLS)) has to be enforced in class Form_FILES; as two of
its columns are involved, the needed event is Form_BeforeUpdate too, so the
following code should be added to the above method before Exit Sub (8
points):
'enforces constraint C16
If IsNull(Me!Folder) Then
If Me!Name <> "\" Or Not IsNull(Me!Ext) Then
Cancel = True
MsgBox "Please change Name and/or Ext!", vbCritical, _
"Root folder Name should be '\' and Ext should be " _
& "null…"
End If
Else
If Me!Name = "\" And IsNull(Me!Ext) Then
Cancel = True
MsgBox "Please change Name, Ext, and/or Folder!", _
vbCritical, _
"Only root folders may have Name = '\' and Ext null…"
End If
End If
Its second half ((yLOGIC_DRIVES)(x = RootFolder(y) Folder(x)
NULLS)) has already been enforced by the Lookup SELECT statement for
RootFolder (see above, the AND IsNull(FILES.Folder)second half of
the WHERE clause) (2 points).
- C17: (xFILES)(Folder?(Folder(x)))
359
This constraint has already been enforced by the Lookup SELECT statement
for Folder (see above, the WHERE FILES.[Folder?]clause) (2 points).
- C18: (xFILES)(LogicDrive(Folder(x)) = LogicDrive(x))
Obviously, it has to be enforced in class Form_FILES; as two of its columns
are involved, the needed event is Form_BeforeUpdate too, so the following
code should be added to the above method before Exit Sub (5 points):
'enforces constraint C18
If Not IsNull(Me!Folder) Then
If Me!LogicDrive <> DLookup("LogicDrive", "FILES", "x=" _
& Me!Folder) Then
Cancel = True
Me!Folder.SetFocus
MsgBox "Please change Folder!", vbCritical, _
"Chosen folder belongs to a different logic drive..."
End If
End If
- C19: (xFILES) ((Name(x) “\” Ext(x) NULLS)
RootFolder(LogicDrive(x) NULLS)
Obviously, it has to be enforced in class Form_FILES; as only column is
involved (LogicDrive), apparently, the needed event is LogicDrive_Before-
Update; in fact, as form FILES is used as a linked subform of LOGIC_
DRIVES, LogicDrive values are automatically filled-in by the system, so that
enforcement of this constraint has to be postponed up to the Form_Before-
Update moment; consequently, the following code should be added to the
above method before Exit Sub (6 points):
'enforces constraint C19
If Me!Name <> "\" Or Not IsNull(Me!Ext) Then
If IsNull(DLookup("RootFolder", "LOGIC_DRIVES", "x=" & _
Me!LogicDrive)) Then
Cancel = True
MsgBox "Please first specify the root folder for this " _
& "logic drive!", vbCritical, _
"Unformatted logic drive..."
End If
End If
- C20: LogicDrive RootFolder = 1LOGIC_DRIVES
Obviously, this constraint may be easily enforced in the Form_Current me-
thod of the class Form_LOGIC_DRIVES, by adding a corresponding dyna-
mic filter to the SELECT statement of the combo box RootFolder (for elimi-
nating from the set of all existing root folders all those that belong to other
logical drives than the current one) and then requerying it (note that on the
blank line, the empty set should be computed instead) (7 points):
360
'*************************
Private Sub Form_Current()
'*************************
On Error GoTo err_point
'enforces constraint C20
If Me.NewRecord Then
Me!RootFolder.RowSource = "SELECT x, x FROM FILES WHERE " _
& "x <> x"
Else
Me!RootFolder.RowSource = _
"SELECT FILES.x, [LogicDriveLetter] & ':\...' & " & _
"IIf(IsNull([FILES].[Folder]),'\',[FOLDERS].[Name] & " _
& "IIf(IsNull([FOLDERS].[Ext]),'','.' & " & _
" [FOLDERS].[Ext]) & '\') & [FILES].[Name] & " & _
"IIf(IsNull([FILES].[Ext]),'','.' & [FILES].[Ext]) " & _
"AS Path " & _
"FROM LOGIC_DRIVES INNER JOIN (FILES LEFT JOIN FILES " _
& "AS FOLDERS ON FILES.Folder = FOLDERS.x) " & _
"ON LOGIC_DRIVES.x = FILES.LogicDrive " & _
"WHERE FILES.[Folder?] And IsNull(FILES.Folder) " & _
"AND FILES.LogicDrive=" & Me!x & _
" ORDER BY [LogicDriveLetter] & ':\...' & " & _
"IIf(IsNull([FILES].[Folder]),'\',[FOLDERS].[Name] & " _
& "IIf(IsNull([FOLDERS].[Ext]),'','.' & " & _
" [FOLDERS].[Ext]) & '\') & [FILES].[Name] & " & _
"IIf(IsNull([FILES].[Ext]),'','.' & [FILES].[Ext])"
End If
Me!RootFolder.Requery
Exit Sub
err_point: MsgBox Err.Source & "->" & Err.Description, _
vbCritical, _
"Error in method Form_LOGIC_DRIVES.Form_Current..."
End Sub
- C21: Folder : FILES FILES, acyclic
In order to may able to compute the transitive closure of Folder’s graph, we
first need to create the corresponding results table (1.5 points):
createFolderTransClosure: CREATE TABLE FolderTransClosure(x Long, Folder Long, [Level]
Long);
Then, obviously, this last non-relational constraint has to be enforced in class
Form_FILES; although, apparently, only column Folder is involved, for new
lines the corresponding logic drive might not be yet specified; consequently,
the needed event for enforcing this constraint is Form_BeforeUpdate, where
the following code should be added before Exit Sub (30.5 points):
'enforces constraint C21
If Not Me.NewRecord And Me!Folder = Me!Folder.OldValue Then _
GoTo exit_point
If Me!Folder = Me!x Then 'irreflexivity
361
Cancel = True
Me!Folder.Undo
MsgBox "Please change Folder!", _
vbCritical, "No folder may belong to itself..."
Else 'acyclicity
Dim level As Integer
Dim oldcard, card As Double
DoCmd.RunSQL "DELETE FROM FolderTransClosure"
oldcard = 0
DoCmd.RunSQL "INSERT INTO FolderTransClosure(x, Folder, " _
& "[Level]) SELECT x, Folder, 0 FROM FILES WHERE " & _
"[Folder?] AND LogicDrive=" & Me!LogicDrive
If Me.NewRecord Then
If IsNull(Me!Folder) Then
DoCmd.RunSQL "INSERT INTO FolderTransClosure(x, Fol" _
& "der, [Level]) VALUES (" & Me!x & ", NULL, 0)"
Else
DoCmd.RunSQL "INSERT INTO FolderTransClosure(x, Fol" _
& "der, [Level]) VALUES (" & Me!x & ", " & _
Me!Folder & ", 0)"
End If
Else
If IsNull(Me!Folder) Then
DoCmd.RunSQL "UPDATE FolderTransClosure SET Folder=" _
& "NULL WHERE x=" & Me!x
Else
DoCmd.RunSQL "UPDATE FolderTransClosure SET Folder=" _
& Me!Folder & " WHERE x=" & Me!x
End If
End If
card = DCount("*", "FolderTransClosure")
level = 1
While oldcard <> card And Not Cancel
oldcard = card
DoCmd.RunSQL "INSERT INTO FolderTransClosure(x, Folder" _
& ", [Level]) SELECT FolderTransClosure.x, FILES." _
& "Folder, " & level & _
" FROM FolderTransClosure INNER JOIN FILES " & _
"ON FolderTransClosure.Folder = FILES.x " & _
"WHERE [Folder?] AND LogicDrive=" & Me!LogicDrive & _
" AND [Level]=" & level - 1
If Not IsNull(DLookup("x", "FolderTransClosure", _
"x = Folder")) Then
Cancel = True
Me!Folder.Undo
MsgBox "Please change Folder as this one would " _
& "create a cycle!", vbCritical, _
"Folders tree hierarchy should be cycle-free..."
Else
card = DCount("*", "FolderTransClosure")
level = level + 1
362
End If
Wend
End If
Total points with Access implementation: 200.
11.2.2 Oracle implementation
Applying the algorithm for translating relational schemes into Oracle dbs,
the following three DDL SQL statements are needed:
Total points with Oracle implementation: 200.
11.3 Best practice rules BPRR1. For any Access report, first design, test, and save independently
its subjacent query; then you can embed it in the report, by replacing the
query name with its body.
BPRR2. For any Access report, do not order the subjacent query: report
grouping and sorting might conflict with your initial order and, anyhow,
ARGM sorts as fast as the ORDER BY clause.
BPRR3. For any Access report, do not forget to program the No Data
event, such that empty reports are replaced by corresponding information
messages.
11.4 Homework H11.1 Modify the report from P11.1 above such that:
- Countries and states are printed first in the descending order of their
population and only then ascendingly on their names.
- The following new computed data is added (both in absolute and per-
centage values): unaccounted cities populations per states, unaccoun-
ted states populations per countries, and unaccounted world popula-
tion.
H11.2 Consider the following E-RD (on mountains) from the GeografieBD
db (where VÂRFURI = PEAKS, MASIVE = MOUNTAINS, GRUPE_MUNTI
= MOUNTAIN_GROUPS, SUBLANTURI = SUBCHAINS, LanturiMuntoase
363
= MOUNTAIN_CHAINS, CONTINENTE = CONTINENTS, Suprafata =
Area, Lungime = Length, Altitudine = Altitude, AltPunctCulminant = High-
estAltitude, LantMuntos = MountainChainName, Vârf = PeakName, Tara =
Country, DenLocala = LocalPeakName):
Figure 11.38 GeografieBD mountains chain
Design, implement in Access, and test a report for printing continents names,
area, total mountain area (absolute and percent of the continent area), and
highest altitude (as the outer group, sorted by names), mountain chains
names and areas (second group, sorted by area descendingly and then by
names ascendingly), mountain sub-chains names, length, and areas (third
group, sorted by area and length descendingly and then by names ascending-
ly), mountain groups names, length, and areas (fourth group, sorted by area
and length descendingly and then by names ascendingly), mountains names,
length, and areas (fifth group, sorted by area and length descendingly and
then by names ascendingly), and peaks names, local names, altitude and
country (sorted descendingly by altitude and then ascendingly by country
and peak name), only for peaks having at least 2,000m altitude, with totals of
number of peaks and average of their altitude per country, mountain, group,
sub-chain, chain, and continent, totals of sum of length per group, sub-chain,
and chain, totals of sum of area per group, sub-chain, chain, and continent, as
well as a grand total of all of this data.
NOTE: Do not forget that next week you will have to pass your second
DB test, on db scheme design and implementation!
364
Chapter 12. 12th Lab: Second Test (DB Design &
Implementation)
12.1 Subject 1 Consider the countries and territories of the world, for which only English
name, official name, suzerain power, capital city (name and country), GDP,
population, and area are of interest. Hint: only very big cities are of interest.
Design the db fragment needed for storing this data and implement it on the
platform of your choice; also add at least two plausible rows per table. Total
points: 180, 2h.
Solution:
Obviously, the corresponding E-RD (10 points) and restrictions list (8
points) are the following:
EnglishName OfficialName GDP Population Area
Suzerain COUNTRIES
Capital Country
CITIES City
COUNTRIES (The set of all existing countries and territories of interest)
Max. cardinality: 250 (R0)
Ranges:
- EnglishName and OfficialName: ASCII(255) (R1)
- Population: [100,…, 2,000,000] (R2)
- GDP (US$ billions) and Area (square km.): [0.1, …, 20,000,000] (R3)
Mandatory: EnglishName (R4)
Uniqueness: Capital (no city may simultaneously be the capital of more
than one country) (R5)
CITIES (The set of all world cities of interest)
Max. cardinality: 1,000,000 (R6)
Ranges: City: ASCII(255) (R7)
Mandatory: City and Country (R8)
Uniqueness: there may not be two (very big) cities of a same country
365
having same names (R9)
The result of applying the translation algorithm from E-RDs and restriction
lists into mathematical schemes is the following (12 points):
COUNTRIES
x NAT(3), total (according to R0)
EnglishName ASCII(255), total (according to R1 and R4)
OfficialName ASCII(255) (according to R1)
GDP [0.1; 20,000,000] (according to R2)
Area [0.1; 20,000,000] (according to R2)
Population [10; 2,000,000,000] (according to R3)
Suzerain : COUNTRIES COUNTRIES
CITIES
x NAT(6), total (according to R6)
City ASCII(255), total (according to R7 and R8)
Capital : COUNTRIES CITIES (according to R5)
Country : CITIES COUNTRIES, total (according to R8)
C9: City Country key (according to R9)
The results of applying the first refinement algorithm (for assisting sets,
functions, and constraints design) are the following (18 points):
a. Sets: both COUNTRIES and CITIES are atomic type object sets not
semantically overloaded.
b. Functions:
- EnglishName is well defined as any country has an English name and
only one;
one-to-oneness: there may no be two countries having same English
name constraint EnglishName key should be added to the db
scheme;
ontoness: not any combination of 255 ASCII characters should be a
country English name;
- OfficialName is well defined as any country has an official name and
only one;
one-to-oneness: there may no be two countries having same official
name constraint OfficialName key should be added to the db
scheme;
366
ontoness: not any combination of 255 ASCII characters should be a
country official name;
- GDP is well defined as any country has one associated GDP and only
one;
one-to-oneness: there may be two countries having same GDP;
ontoness: not any number between 0.1 and 20,000,000 should be the
GDP of a country;
- Population is well defined as any country has one associated total
population number and only one;
one-to-oneness: there may be two countries having same population;
ontoness: not any number between 10 and 20,000,000,000 should be
the population of a country;
- Area is well defined as any country has one associated area and only
one;
one-to-oneness: there may be two countries having same area;
ontoness: not any number between 0.1 and 20,000,000 should be the
area of a country;
- Suzerain is well defined as any subordinated country (territory) has
one suzerain power and only one;
one-to-oneness: there may be two countries having same suzerain;
ontoness: not any country is a suzerain power;
homogeneous binary relationship constraints: no country may be its
own suzerain power, either directly or indirectly constraint Suze-
rain acyclic should be add to the db scheme;
- Capital is well defined as any country has one capital city and only
one;
one-to-oneness: no city may simultaneously be the capital of more
than one country;
ontoness: not any city should be a country capital;
- City is well defined as any city has one name and only one;
one-to-oneness: there may be two cities having same name (but in
different countries);
ontoness: not any combination of 255 ASCII characters should be a
city name;
- Country is well defined as any city belongs to one country and only
one;
367
one-to-oneness: there may be two cities belonging to a same country;
ontoness: not any country should have a city (in the db’s instance);
c. Constraints: no other constraints apply to this subuniverse.
The results of applying the second refinement algorithm (for assisting keys
discovery) are the following (8 points):
COUNTRIES
n = 4 (GDP, Population, Area, Suzerain), K = {Capital, EnglishName,
OfficialName};
nonprimeness: obviously, GDP, Population, and Area are all nonprime, as
they might not contribute to countries’ unique identification;
n’ = 1 (Suzerain) nothing to do as Suzerain is not one-to-one.
CITIES
n = 2 (City, Country), K = {City Country};
nothing to do as neither City, nor Country is not one-to-one.
Consequently, neither COUNTRIES, nor CITIES have other semantic keys.
The results of applying the third refinement algorithm (for assisting analysis
of E-RD cycles) are the following (6 points):
There are two cycles in this E-RD: Suzerain (that was already investigated
above) and the unidirectional one made out of Capital : COUNTRIES
CITIES and Country : CITIES COUNTRIES. For this latter one, possible
local commutativities should be investigated in both of its nodes:
- Capital Country =? 1CITIES (should any city be the capital of the
country to which belongs?); obviously no, as not every city is a capi-
tal of its country;
- Country Capital =? 1COUNTRIES (should any capital city of a country
belong to that country?); obviously yes, as no country may have as its
capital a city from another country constraint Country Capital =
1COUNTRIES should be added to the db scheme.
The finally obtained mathematical scheme is the following (15 points):
COUNTRIES
368
x NAT(3), total
EnglishName ASCII(255), total
OfficialName ASCII(255)
GDP [0.1; 20,000,000]
Area [0.1; 20,000,000]
Population [10; 2,000,000,000]
Suzerain : COUNTRIES COUNTRIES, acyclic
CITIES
x NAT(6), total
City ASCII(255), total
Capital : COUNTRIES CITIES
Country : CITIES COUNTRIES, total
C9: City Country key
C10: Country Capital = 1COUNTRIES
Applying the algorithm for translating mathematical schemes into relational
ones (23 points) and non-relational constraint lists (2 points) yields the fol-
lowing:
COUNTRIES (x, Capital, EnglishName, OfficialName)
x English-
Name
Official-
Name
GDP Area Popu
la-
tion
Suze
rain
Capi-
tal
auton(
3)
ASCII(255) ASCII(255) [0.1,
2*107
]
[0.1,
2*107
]
[10;
2*109]
Im
(x)
Im(CI-
TIES.x
)
NOT
NULL
NOT
NULL
1 U.K. United
Kindom
1
2 Canada Canada 1 3
369
CITIES (x, City Country)
x City Country
auoton(6) ASCII(255) Im(COUNTRIES.x)
NOT NULL NOT NULL NOT NULL
1 London 1
2 Toronto 2
3 Ottawa 2
The corresponding non-relational constraint list is the following (2 points):
C10: Country Capital = 1COUNTRIES
C11: Suzerain : COUNTRIES COUNTRIES, acyclic
12.2.1 Access implementation
Applying the algorithm for translating relational schemes into Access dbs,
the following three DDL SQL statements are needed (18 points):
createCOUNTRIES:
CREATE TABLE COUNTRIES (x COUNTER PRIMARY KEY,
EnglishName VARCHAR(255) NOT NULL UNIQUE,
OfficialName VARCHAR(255) UNIQUE, GDP Double, Area
Double, Population Double, Capital Long UNIQUE,
Suzerain LONG, CONSTRAINT fkSuzerain FOREIGN KEY
(Suzerain) REFERENCES COUNTRIES);
createCITIES:
CREATE TABLE CITIES (x COUNTER PRIMARY KEY, City
VARCHAR(255) NOT NULL, Country Long NOT NULL,
CONSTRAINT fkCountry FOREIGN KEY (Country)
REFERENCES COUNTRIES, CONSTRAINT kCITIES UNIQUE
(City, Country));
alterCOUNTRIES:
ALTER TABLE COUNTRIES ADD CONSTRAINT fkCapital
FOREIGN KEY (Capital) REFERENCES CITIES;
Through Access’ GUI, the following has to to be added to this db (14
points):
370
- For all text columns (EnglishName, OfficialName, and Country) cor-
responding Allow zero length property should be turned to No (for
rejecting dirty nulls).
- For all numeric ones (GDP, Area, Population) that have as (co-)do-
mains subsets of Access’ data types, their Validation Rule and Text
properties should be filled accordingly:
GDP and Area: Between 0.1 and 2*10^7 (“Valid
GDP/Area values are between 0.1 and 2*10^7!”);
Population: Between 10 and 2*10^9 (“Valid Popula-
tion values are between 10 and 2*10^9!”);
- For all foreign keys (Suzerain, Capital, Country), corresponding text
boxes should be replaced by combo ones, for hiding pointers and dis-
playing instead corresponding semantic key values:
Suzerain and Country: SELECT x, EnglishName FROM COUNTRIES
ORDER BY EnglishName;
Capital: SELECT CITIES.x, [City] & ", " & [EnglishName] AS Capital FROM COUNTRIES INNER JOIN CITIES
ON COUNTRIES.x = CITIES.Country
ORDER BY [City] & ", " & [EnglishName];
For populating the two tables with their demos rows the following seven
SQL DML statements are needed (7.5 points): INSERT INTO COUNTRIES(EnglishName, OfficialName)
VALUES ("U.K.", "United Kingdom");
INSERT INTO COUNTRIES(EnglishName, OfficialName, Suzerain)
VALUES ("Canada", "Canada", 1);
INSERT INTO CITIES(City, Country) VALUES ("London", 1);
INSERT INTO CITIES(City, Country) VALUES ("Toronto", 2);
INSERT INTO CITIES(City, Country) VALUES ("Ottawa", 2);
UPDATE COUNTRIES SET Capital = 1 WHERE x = 1;
UPDATE COUNTRIES SET Capital = 3 WHERE x = 2;
According to the algorithm for assisting enforcement of non-relational con-
straints in Access, in order to enforce the above non-relational constraints we
first have to create (by using the Form Wizard) the datasheet form COUN-
TRIES (1 point).
- C10: Country Capital = 1COUNTRIES
371
Obviously, this constraint may be easily enforced in the Form_Current me-
thod of the class Form_COUNTRIES, by adding a corresponding dynamic
filter to the SELECT statement of the combo box Capital (for eliminating
from the set of all existing cities all those that belong to other countries than
the current one) and then requerying it (note that on the blank line, the empty
set should be computed instead) (5 points):
'*************************
Private Sub Form_Current()
'*************************
On Error GoTo err_point
'enforces constraint C10
If Me.NewRecord Then
Me!Capital.RowSource = "SELECT x, x FROM COUNTRIES WHERE" _
& " x <> x;"
Else
Me!Capital.RowSource = _
"SELECT CITIES1.x, [City] & ', ' & [EnglishName] " & _
"AS Capital FROM COUNTRIES1 INNER JOIN CITIES1 " & _
"ON COUNTRIES1.x = CITIES1.Country " & _
"WHERE COUNTRIES1.x=" & Me!x & _
" ORDER BY [City] & ', ' & [EnglishName];"
End If
Me!Capital.Requery
Exit Sub
err_point: MsgBox Err.Source & "->" & Err.Description, _
vbCritical, _
"Error in method Form_COUNTRIES.Form_Current..."
End Sub
- C11: Suzerain : COUNTRIES COUNTRIES, acyclic
In order to may able to compute the transitive closure of Suzerain’s graph,
we first need to create the corresponding results table (1.5 points):
createSuzerainTransClosure: CREATE TABLE FolderTransClosure(x Long, Suzerain Long,
[Level] Long);
Then, obviously, this last non-relational constraint has to be enforced in class
Form_COUNTRIES; as only column Suzerain is involved, the needed event
for enforcing this constraint is Suzerain_BeforeUpdate: the following code
should be added to this class (31 points):
'***************************************************
Private Sub Suzerain_BeforeUpdate(Cancel As Integer)
'***************************************************
On Error GoTo err_point
'enforces constraint C11
If Not Me.NewRecord And Me!Suzerain = Me!Suzerain.OldValue _
Then GoTo exit_point
If Me!Suzerain = Me!x Then 'irreflexivity
372
Cancel = True
Me!Suzerain.Undo
MsgBox "Please change Suzerain!", _
vbCritical, "No country may be its own suzerain..."
Else 'acyclicity
Dim level As Integer
Dim oldcard, card As Double
DoCmd.RunSQL "DELETE FROM SuzerainTransClosure"
oldcard = 0
DoCmd.RunSQL "INSERT INTO SuzerainTransClosure(x, Suze" _
& "rain, [Level]) SELECT x, Suzerain, 0 FROM COUNTRIES"
If Me.NewRecord Then
If IsNull(Me!Suzerain) Then
DoCmd.RunSQL "INSERT INTO SuzerainTransClosure(x, " _
& "Suzerain, [Level]) VALUES (" & Me!x & ", NULL, 0)"
Else
DoCmd.RunSQL "INSERT INTO SuzerainTransClosure(x, " _
& "Suzerain, [Level]) VALUES (" & Me!x & ", " & _
Me!Suzerain & ", 0)"
End If
Else
If IsNull(Me!Suzerain) Then
DoCmd.RunSQL "UPDATE SuzerainTransClosure " _
& " SET Suzerain = NULL WHERE x=" & Me!x
Else
DoCmd.RunSQL "UPDATE SuzerainTransClosure " _
& "SET Suzerain =" & Me!Suzerain & " WHERE x=" & Me!x
End If
End If
card = DCount("*", "SuzerainTransClosure")
level = 1
While oldcard <> card And Not Cancel
oldcard = card
DoCmd.RunSQL "INSERT INTO SuzerainTransClosure(x, " _
& "Suzerain,[Level]) SELECT SuzerainTransClosure.x," _
& "COUNTRIES.Suzerain, " & level & _
" FROM SuzerainTransClosure INNER JOIN COUNTRIES " & _
"ON SuzerainTransClosure.Suzerain = COUNTRIES.x " & _
"WHERE [Level]=" & level - 1
If Not IsNull(DLookup("x", "SuzerainTransClosure", _
"x = Suzerain")) Then
Cancel = True
Me!Suzerain.Undo
MsgBox "Please change Suzerain as this one would " _
& "create a cycle!", vbCritical, "Suzerains " _
& "tree hierarchy should be cycle-free..."
Else
card = DCount("*", "SuzerainTransClosure")
level = level + 1
End If
Wend
373
End If
exit_point: Exit Sub
err_point: MsgBox Err.Source & "->" & Err.Description, _
vbCritical, _
"Error in method Form_COUNTRIES.Capital_BeforeUpdate..."
Cancel = True
End Sub
12.1.2 Oracle implementation
Applying the algorithm for translating relational schemes into Oracle dbs,
the following three DDL SQL statements are needed:
12.2 Subject 2
12.2.1 Access implementation
Applying the algorithm for translating relational schemes into Access dbs,
the following three DDL SQL statements are needed:
12.2.2 Oracle implementation
Applying the algorithm for translating relational schemes into Oracle dbs,
the following three DDL SQL statements are needed:
374
Chapter 13. 13th Lab: Test Redoing (SQL or DB Design &
Implementation)
13.1 SQL test redoing
90 minutes, 45 points, open book, individual, no electronic devices
a. (7p.)
Consider the following table:
RIVERS(x, River)
x River Length
auton(8) ASCII(64) [1, 7000]
NOT NULL NOT NULL
1 Danube 2850
2 Olt
3 Lotru
Design and develop a SQL script for creating this table including its instance
and then for also adding to it the rivers “Paraguay” and “Parana” (Hint: do
not let the system to automatically name the semantic key, but name it your-
self!).
b. (14p.) Design and develop a SQL script for adding the function Tributa-
ryTo : RIVERS RIVERS, for changing the key to River TributaryTo, and
then storing the facts that “Olt” is tributary to the “Danube”, “Lotru” to
“Olt”, and “Paraguay” to “Parana”, but without using the x values for none
of them.
c. (Only in Access)(6p.) Describe needed steps for converting TributaryTo to
a lookup combo-box, without using the Lookup Wizard.
c. (Only in Oracle) (3.5p.)
d. (18p Access/18p Oracle) Compute in SQL, without using subqueries, the
set of triples <River, TributariesNo, GrandTributariesNo> (Hint: “Lotru” is
a great tributary to the “Danube”, for example), in the descending order of
TributariesNo and GrandTributariesNo, and then ascending on River, for all
rivers that have at least k tributary ones, each of which having at least n
375
tributaries (k and n being natural parameters). Which is the result for the
above RIVERS instance when k = 1, n = 0; k = n = 1; and k = 1, n = 2?
6.1.1 Access solution
a. CREATE TABLE RIVERS(x COUNTER PRIMARY KEY, River VARCHAR(64)
NOT NULL, Length LONG, CONSTRAINT kRIVERS UNIQUE (River));
(4.5p) INSERT INTO RIVERS(River, Length) VALUES ("Danube", 2850);
(0.5p)
INSERT INTO RIVERS(River) VALUES ("Olt"); (0.5p)
INSERT INTO RIVERS(River) VALUES ("Lotru"); (0.5p)
INSERT INTO RIVERS(River) VALUES ("Paraguay"); (0.5p)
INSERT INTO RIVERS(River) VALUES ("Parana"); (0.5p)
b.
ALTER TABLE RIVERS ADD COLUMN TributaryTo LONG; (1.5p)
ALTER TABLE RIVERS ADD CONSTRAINT fkTributaryTo FOREIGN KEY (TributaryTo) REFERENCES RIVERS; (2p)
ALTER TABLE RIVERS DROP CONSTRAINT kRIVERS; (1p)
ALTER TABLE RIVERS ADD CONSTRAINT kRIVERS UNIQUE (River,
TributaryTo)); (2p) UPDATE RIVERS SET TributaryTo = DLookup("x", "RIVERS",
"River='Danube'") WHERE River = "Olt"; (2.5p) UPDATE RIVERS SET TributaryTo = DLookup("x", "RIVERS",
"River='Olt'") WHERE River = "Lotru"; (2.5p)
UPDATE RIVERS SET TributaryTo = DLookup("x", "RIVERS",
"River='Parana'") WHERE River = "Paraguay"; (2.5p)
c.
- open RIVERS in Design mode; (0.5p)
- on the TributaryTo line, in the Lookup tab, switch the Display Control
property from Text to Combo box; (0.5p)
- in the Row Source property, type:
SELECT x, River FROM RIVERS ORDER BY River; (2p)
- set the Column Count property to 2; (0.5p)
- set the Column Width property to 0”;4”; (0.5p)
- set the List Rows property to 32; (0.5p)
- set the List Width property to 4”; (0.5p)
376
- set the Limit to List property to Yes; (0.5p)
- save and close the table scheme window. (0.5p)
d.
- step 1: rivers with at least k tributary ones
RiversHavingAtLeastkTributaries: (5p)
SELECT RIVERS.x, Count(CHILDREN.x) AS TributariesNo
FROM RIVERS AS CHILDREN INNER JOIN RIVERS
ON CHILDREN.TributaryTo = RIVERS.x
GROUP BY RIVERS.x
HAVING Count(CHILDREN.x) >=
[Enter desired minimum number of tributaries:];
- step 2: rivers with at least k tributary ones, each of which having n
tributaries
RiversHavingAtLeastkTributariesAndnGrandTributaries: (7p)
SELECT RiversHavingAtLeastkTributaries.x,
RiversHavingAtLeastkTributaries.TributariesNo,
Count(GrandChildren.x) AS GrandTributariesNo
FROM RIVERS AS GrandChildren INNER JOIN (RIVERS AS CHILDREN
INNER JOIN RiversHavingAtLeastkTributaries
ON CHILDREN.TributaryTo =
RiversHavingAtLeastkTributaries.x)
ON GrandChildren.TributaryTo = CHILDREN.x
GROUP BY RiversHavingAtLeastkTributaries.x,
RiversHavingAtLeastkTributaries.TributariesNo
HAVING Count(GrandTributariesNo.x) >=
[Enter desired minimum number of grand tributaries:];
- step 3: getting names and sorting
Test-1redo: (3p)
SELECT River, TributariesNo, GrandTributariesNo
FROM RIVERS INNER JOIN
RiversHavingAtLeastkTributariesAndnGrandTributaries
377
ON RIVERS.x =
RiversHavingAtLeastkTributariesAndnGrandTributaries.x
ORDER BY ChildrenNo DESC, GrandChildrenNo DESC, River;
Result of query Test-1redo for k = 1, n = 0 (1p)
River TributariesNo GrandTributariesNo
Danube 1 1
Olt 1 0
Parana 1 0
Result of query Test-1redo for k = 1, n = 1 (1p)
River TributariesNo GrandTributariesNo
Danube 1 1
Result of query Test-1redo for k = 1, n = 2 (1p)
River TributariesNo GrandTributariesNo
6.1.2 Oracle solution
a.
INSERT INTO STATES (State, Country)
VALUES ('Prahova', 1); (0.5p)
INSERT INTO CITIES (City, State)
SELECT 'Sinaia', x FROM STATES
WHERE State = 'Prahova' AND Country = 1; (1.5p)
b.
ALTER TABLE PEOPLE ADD BirthPlace NUMBER(6,0);
(0.5p)
ALTER TABLE PEOPLE ADD CONSTRAINT fkBPlace
FOREIGN KEY (BirthPlace) REFERENCES CITIES;
(1.25p)
378
UPDATE PEOPLE SET BirthPlace = (SELECT x FROM
CITIES WHERE City='Sinaia') WHERE x IN (7, 12);
(1.75p)
c.
-------------------------------------------------
-- DDL for View CITIES_STATES_CONTRIES
-------------------------------------------------
CREATE OR REPLACE FORCE VIEW
"LAB_DB"."CITIES_STATES_CONTRIES"
("X", "City, State, Country") AS
SELECT CITIES.x, City || ', ' || STATES.State ||
', ' || COUNTRIES.Country "City, State, Country"
FROM COUNTRIES INNER JOIN (STATES INNER JOIN
CITIES ON STATES.x = CITIES.State)
ON COUNTRIES.x = STATES.Country
ORDER BY City || ', ' || STATES.State || ', ' ||
COUNTRIES.Country; (3.5p)
d.
- step 0: create a temporary table for storing fathers with at least k children
(1p)
-------------------------------------------------
-- DDL for Table TMPPHLKC
-------------------------------------------------
CREATE TABLE "LAB_DB"."TMPPHLKC"
( "X" NUMBER(6,0),
"CHILDRENNO" NUMBER(2,0)
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1
MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT
CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS";
379
- step 1: add to the LAB_DB_PL_SQL package the following procedure:
procedure AtLeastkChildrennGrandChildren
(Min_no_of_children in integer,
Min_no_of_grand_children in integer,
rc out GenericCursorType);
procedure AtLeastkChildrennGrandChildren
(Min_no_of_children in integer,
Min_no_of_grand_children in integer,
rc out GenericCursorType) is
begin
--0. initialize tmpPHLkC (0.5p) DELETE FROM tmpPHLkC;
--1. compute and store into it fathers with at least k
--children (4.5p) INSERT INTO tmpPHLkC
SELECT PEOPLE.x, Count(CHILDREN.x) ChildrenNo
FROM PEOPLE CHILDREN INNER JOIN PEOPLE
ON CHILDREN.Father = PEOPLE.x
GROUP BY PEOPLE.x
HAVING Count(CHILDREN.x) >= Min_no_of_children;
--2. compute and return grandfathers with at least k
--children, each of which has n children (9p) OPEN rc FOR
SELECT NAMES.Name, ChildrenNo,
Count(GrandChildren.x) GrandChildrenNo
FROM (PEOPLE GrandChildren INNER JOIN (PEOPLE CHILDREN INNER
JOIN tmpPHLkC
ON CHILDREN.Father = tmpPHLkC.x)
ON GrandChildren.Father = CHILDREN.x) INNER JOIN PEOPLE
NAMES ON tmpPHLkC.x = NAMES.x
GROUP BY NAMES.Name, tmpPHLkC.ChildrenNo
HAVING Count(GrandChildren.x) >= Min_no_of_grand_children
ORDER BY ChildrenNo DESC, Count(GrandChildren.x) DESC,
NAMES.Name;
end AtLeastkChildrennGrandChildren;
- step 3: test for k = n = 1, respectively 2
var c refcursor;
exec LAB_DB_PL_SQL.AtLeastkChildrennGrandChildren(1,1,:c);
print c;
380
Figure 6.3 Result of stored procedure AtLeastkChildrennGrandChildren
for k = n = 1 (1p)
381
exec LAB_DB_PL_SQL.AtLeastkChildrennGrandChildren(2,2,:c);
print c;
Figure 6.4 Result (empty set) of stored procedure
AtLeastkChildrennGrandChildren for k = n = 2 (1p)
382
13.2 DB design and implementation test redoing Consider the countries and territories of the world, for which only English
name, official name, suzerain power, capital city (name and country), GDP,
population, and area are of interest (Hint: only very big cities are of interest;
note that the world is a tree of states and territories, rooted in the U.N.,
including superstates like the E.U. or the C.I.S., ordinary states, and “sub-
states” like colonies, protectorates, etc.; for example, the E.U. is the suzerain
power of France, which, in its turn, is the suzerain power of the Kerguelen
Islands). Design the db fragment needed for storing this data and implement
it on the platform of your choice; also add at least two plausible rows per
table. Total points: 180, 2h.
Solution:
Obviously, the corresponding E-RD (10 points) and restrictions list (8
points) are the following:
EnglishName OfficialName GDP Population Area
Suzerain COUNTRIES
Capital Country
CITIES City
COUNTRIES (The set of all existing countries and territories of interest)
Max. cardinality: 250 (R0)
Ranges:
- EnglishName and OfficialName: ASCII(255) (R1)
- Population: [100,…, 2,000,000] (R2)
- GDP (US$ billions) and Area (square km.): [0.1, …, 20,000,000] (R3)
Mandatory: EnglishName (R4)
Uniqueness: Capital (no city may simultaneously be the capital of more
than one country), EnglishName (there may not be two countries
having same English name), and OfficialName (there may not be
two countries having same official name) (R5)
CITIES (The set of all world cities of interest)
Max. cardinality: 1,000,000 (R6)
383
Ranges: City: ASCII(255) (R7)
Mandatory: City and Country (R8)
Uniqueness: there may not be two (very big) cities of a same country
having same names (R9)
The result of applying the translation algorithm from E-RDs and restriction
lists into mathematical schemes is the following (12 points):
COUNTRIES
x NAT(3), total (according to R0)
EnglishName ASCII(255), total (according to R1 and R4)
OfficialName ASCII(255) (according to R1)
GDP [0.1; 20,000,000] (according to R2)
Area [0.1; 20,000,000] (according to R2)
Population [10; 2,000,000,000] (according to R3)
Suzerain : COUNTRIES COUNTRIES
CITIES
x NAT(6), total (according to R6)
City ASCII(255), total (according to R7 and R8)
Capital : COUNTRIES CITIES (according to R5)
Country : CITIES COUNTRIES, total (according to R8)
C9: City Country key (according to R9)
The results of applying the first refinement algorithm (for assisting sets,
functions, and constraints design) are the following (18 points):
d. Sets: both COUNTRIES and CITIES are atomic type object sets not
semantically overloaded.
e. Functions:
- EnglishName is well defined as any country has an English name and
only one;
one-to-oneness: there may no be two countries having same English
name constraint EnglishName key should be added to the db
scheme;
ontoness: not any combination of 255 ASCII characters should be a
country English name;
- OfficialName is well defined as any country has an official name and
only one;
384
one-to-oneness: there may no be two countries having same official
name constraint OfficialName key should be added to the db
scheme;
ontoness: not any combination of 255 ASCII characters should be a
country official name;
- GDP is well defined as any country has one associated GDP and only
one;
one-to-oneness: there may be two countries having same GDP;
ontoness: not any number between 0.1 and 20,000,000 should be the
GDP of a country;
- Population is well defined as any country has one associated total
population number and only one;
one-to-oneness: there may be two countries having same population;
ontoness: not any number between 10 and 20,000,000,000 should be
the population of a country;
- Area is well defined as any country has one associated area and only
one;
one-to-oneness: there may be two countries having same area;
ontoness: not any number between 0.1 and 20,000,000 should be the
area of a country;
- Suzerain is well defined as any subordinated country (territory) has
one suzerain power and only one;
one-to-oneness: there may be two countries having same suzerain;
ontoness: not any country is a suzerain power;
homogeneous binary relationship constraints: no country may be its
own suzerain power, either directly or indirectly constraint Suze-
rain acyclic should be add to the db scheme;
- Capital is well defined as any country has one capital city and only
one;
one-to-oneness: no city may simultaneously be the capital of more
than one country;
ontoness: not any city should be a country capital;
- City is well defined as any city has one name and only one;
one-to-oneness: there may be two cities having same name (but in
different countries);
385
ontoness: not any combination of 255 ASCII characters should be a
city name;
- Country is well defined as any city belongs to one country and only
one;
one-to-oneness: there may be two cities belonging to a same country;
ontoness: not any country should have a city (in the db’s instance);
f. Constraints: no other constraints apply to this subuniverse.
The results of applying the second refinement algorithm (for assisting keys
discovery) are the following (8 points):
COUNTRIES
n = 4 (GDP, Population, Area, Suzerain), K = {Capital, EnglishName,
OfficialName};
nonprimeness: obviously, GDP, Population, and Area are all nonprime, as
they might not contribute to countries’ unique identification;
n’ = 1 (Suzerain) nothing to do as Suzerain is not one-to-one.
CITIES
n = 2 (City, Country), K = {City Country};
nothing to do as neither City, nor Country is not one-to-one.
Consequently, neither COUNTRIES, nor CITIES have other semantic keys.
The results of applying the third refinement algorithm (for assisting analysis
of E-RD cycles) are the following (6 points):
There are two cycles in this E-RD: Suzerain (that was already investigated
above) and the unidirectional one made out of Capital : COUNTRIES
CITIES and Country : CITIES COUNTRIES. For this latter one, possible
local commutativities should be investigated in both of its nodes:
- Capital Country =? 1CITIES (should any city be the capital of the
country to which belongs?); obviously no, as not every city is a capi-
tal of its country;
- Country Capital =? 1COUNTRIES (should any capital city of a country
belong to that country?); obviously yes, as no country may have as its
386
capital a city from another country constraint Country Capital =
1COUNTRIES should be added to the db scheme.
The finally obtained mathematical scheme is the following (15 points):
COUNTRIES
x NAT(3), total
EnglishName ASCII(255), total
OfficialName ASCII(255)
GDP [0.1; 20,000,000]
Area [0.1; 20,000,000]
Population [10; 2,000,000,000]
Suzerain : COUNTRIES COUNTRIES, acyclic
CITIES
x NAT(6), total
City ASCII(255), total
Capital : COUNTRIES CITIES
Country : CITIES COUNTRIES, total
C9: City Country key
C10: Country Capital = 1COUNTRIES
Applying the algorithm for translating mathematical schemes into relational
ones (23 points) and non-relational constraint lists (2 points) yields the fol-
lowing:
COUNTRIES (x, Capital, EnglishName, OfficialName)
x English-
Name
Official-
Name
GDP Area Popu
la-
tion
Suze
rain
Capi-
tal
auton(
3)
ASCII(255) ASCII(255) [0.1,
2*107
]
[0.1,
2*107
]
[10;
2*109]
Im
(x)
Im(CI-
TIES.x
)
NOT
NULL
NOT
NULL
1 U.K. United
Kindom
1
2 Canada Canada 1 3
387
CITIES (x, City Country)
x City Country
auoton(6) ASCII(255) Im(COUNTRIES.x)
NOT NULL NOT NULL NOT NULL
1 London 1
2 Toronto 2
3 Ottawa 2
The corresponding non-relational constraint list is the following (2 points):
C10: Country Capital = 1COUNTRIES
C11: Suzerain : COUNTRIES COUNTRIES, acyclic
13.2.1 Access implementation
Applying the algorithm for translating relational schemes into Access dbs,
the following three DDL SQL statements are needed (18 points):
createCOUNTRIES:
CREATE TABLE COUNTRIES (x COUNTER PRIMARY KEY,
EnglishName VARCHAR(255) NOT NULL UNIQUE,
OfficialName VARCHAR(255) UNIQUE, GDP Double, Area
Double, Population Double, Capital Long UNIQUE,
Suzerain LONG, CONSTRAINT fkSuzerain FOREIGN KEY
(Suzerain) REFERENCES COUNTRIES);
createCITIES:
CREATE TABLE CITIES (x COUNTER PRIMARY KEY, City
VARCHAR(255) NOT NULL, Country Long NOT NULL,
CONSTRAINT fkCountry FOREIGN KEY (Country)
REFERENCES COUNTRIES, CONSTRAINT kCITIES UNIQUE
(City, Country));
alterCOUNTRIES:
ALTER TABLE COUNTRIES ADD CONSTRAINT fkCapital
FOREIGN KEY (Capital) REFERENCES CITIES;
Through Access’ GUI, the following has to to be added to this db (14
points):
388
- For all text columns (EnglishName, OfficialName, and Country) cor-
responding Allow zero length property should be turned to No (for
rejecting dirty nulls).
- For all numeric ones (GDP, Area, Population) that have as (co-)do-
mains subsets of Access’ data types, their Validation Rule and Text
properties should be filled accordingly:
GDP and Area: Between 0.1 and 2*10^7 (“Valid
GDP/Area values are between 0.1 and 2*10^7!”);
Population: Between 10 and 2*10^9 (“Valid Popula-
tion values are between 10 and 2*10^9!”);
- For all foreign keys (Suzerain, Capital, Country), corresponding text
boxes should be replaced by combo ones, for hiding pointers and dis-
playing instead corresponding semantic key values:
Suzerain and Country: SELECT x, EnglishName FROM COUNTRIES
ORDER BY EnglishName;
Capital: SELECT CITIES.x, [City] & ", " & [EnglishName] AS Capital FROM COUNTRIES INNER JOIN CITIES
ON COUNTRIES.x = CITIES.Country
ORDER BY [City] & ", " & [EnglishName];
For populating the two tables with their demos rows the following seven
SQL DML statements are needed (7.5 points): INSERT INTO COUNTRIES(EnglishName, OfficialName)
VALUES ("U.K.", "United Kingdom");
INSERT INTO COUNTRIES(EnglishName, OfficialName, Suzerain)
VALUES ("Canada", "Canada", 1);
INSERT INTO CITIES(City, Country) VALUES ("London", 1);
INSERT INTO CITIES(City, Country) VALUES ("Toronto", 2);
INSERT INTO CITIES(City, Country) VALUES ("Ottawa", 2);
UPDATE COUNTRIES SET Capital = 1 WHERE x = 1;
UPDATE COUNTRIES SET Capital = 3 WHERE x = 2;
According to the algorithm for assisting enforcement of non-relational con-
straints in Access, in order to enforce the above non-relational constraints we
first have to create (by using the Form Wizard) the datasheet form COUN-
TRIES (1 point).
- C10: Country Capital = 1COUNTRIES
389
Obviously, this constraint may be easily enforced in the Form_Current me-
thod of the class Form_COUNTRIES, by adding a corresponding dynamic
filter to the SELECT statement of the combo box Capital (for eliminating
from the set of all existing cities all those that belong to other countries than
the current one) and then requerying it (note that on the blank line, the empty
set should be computed instead) (5 points):
'*************************
Private Sub Form_Current()
'*************************
On Error GoTo err_point
'enforces constraint C10
If Me.NewRecord Then
Me!Capital.RowSource = "SELECT x, x FROM COUNTRIES WHERE" _
& " x <> x;"
Else
Me!Capital.RowSource = _
"SELECT CITIES1.x, [City] & ', ' & [EnglishName] " & _
"AS Capital FROM COUNTRIES1 INNER JOIN CITIES1 " & _
"ON COUNTRIES1.x = CITIES1.Country " & _
"WHERE COUNTRIES1.x=" & Me!x & _
" ORDER BY [City] & ', ' & [EnglishName];"
End If
Me!Capital.Requery
Exit Sub
err_point: MsgBox Err.Source & "->" & Err.Description, _
vbCritical, _
"Error in method Form_COUNTRIES.Form_Current..."
End Sub
- C11: Suzerain : COUNTRIES COUNTRIES, acyclic
In order to may able to compute the transitive closure of Suzerain’s graph,
we first need to create the corresponding results table (1.5 points):
createSuzerainTransClosure: CREATE TABLE FolderTransClosure(x Long, Suzerain Long,
[Level] Long);
Then, obviously, this last non-relational constraint has to be enforced in class
Form_COUNTRIES; as only column Suzerain is involved, the needed event
for enforcing this constraint is Suzerain_BeforeUpdate: the following code
should be added to this class (31 points):
'***************************************************
Private Sub Suzerain_BeforeUpdate(Cancel As Integer)
'***************************************************
On Error GoTo err_point
'enforces constraint C11
If Not Me.NewRecord And Me!Suzerain = Me!Suzerain.OldValue _
Then GoTo exit_point
If Me!Suzerain = Me!x Then 'irreflexivity
390
Cancel = True
Me!Suzerain.Undo
MsgBox "Please change Suzerain!", _
vbCritical, "No country may be its own suzerain..."
Else 'acyclicity
Dim level As Integer
Dim oldcard, card As Double
DoCmd.RunSQL "DELETE FROM SuzerainTransClosure"
oldcard = 0
DoCmd.RunSQL "INSERT INTO SuzerainTransClosure(x, Suze" _
& "rain, [Level]) SELECT x, Suzerain, 0 FROM COUNTRIES"
If Me.NewRecord Then
If IsNull(Me!Suzerain) Then
DoCmd.RunSQL "INSERT INTO SuzerainTransClosure(x, " _
& "Suzerain, [Level]) VALUES (" & Me!x & ", NULL, 0)"
Else
DoCmd.RunSQL "INSERT INTO SuzerainTransClosure(x, " _
& "Suzerain, [Level]) VALUES (" & Me!x & ", " & _
Me!Suzerain & ", 0)"
End If
Else
If IsNull(Me!Suzerain) Then
DoCmd.RunSQL "UPDATE SuzerainTransClosure " _
& " SET Suzerain = NULL WHERE x=" & Me!x
Else
DoCmd.RunSQL "UPDATE SuzerainTransClosure " _
& "SET Suzerain =" & Me!Suzerain & " WHERE x=" & Me!x
End If
End If
card = DCount("*", "SuzerainTransClosure")
level = 1
While oldcard <> card And Not Cancel
oldcard = card
DoCmd.RunSQL "INSERT INTO SuzerainTransClosure(x, " _
& "Suzerain,[Level]) SELECT SuzerainTransClosure.x," _
& "COUNTRIES.Suzerain, " & level & _
" FROM SuzerainTransClosure INNER JOIN COUNTRIES " & _
"ON SuzerainTransClosure.Suzerain = COUNTRIES.x " & _
"WHERE [Level]=" & level - 1
If Not IsNull(DLookup("x", "SuzerainTransClosure", _
"x = Suzerain")) Then
Cancel = True
Me!Suzerain.Undo
MsgBox "Please change Suzerain as this one would " _
& "create a cycle!", vbCritical, "Suzerains " _
& "tree hierarchy should be cycle-free..."
Else
card = DCount("*", "SuzerainTransClosure")
level = level + 1
End If
Wend
391
End If
exit_point: Exit Sub
err_point: MsgBox Err.Source & "->" & Err.Description, _
vbCritical, _
"Error in method Form_COUNTRIES.Capital_BeforeUpdate..."
Cancel = True
End Sub
13.2.2 Oracle implementation
392
Chapter 14. 14th Lab: Projects Defense
393
Conclusion
394
References
1. Mancas, C. Databases Lecture Notes, 2014, Bucharest Polytechnic
University.
2. Mancas, C. Conceptual Data Modeling and Database Design: Ana-
lysis, Implementation and Optimization. A Fully Algorithmic Ap-
proach, 2014, Apple Academic Press.
3. Microsoft Corp. Access 2010 Help.
4. IT Services. Microsoft Access 2010 An Essential Guide (Level 1),
2011, The University of Reading (freely downloadable from
http://www.reading.ac.uk/web/files/its/AccessEssen2010.pdf).
5. IT Services. Microsoft Access 2010 An Intermediate Guide (Level 2),
2011, The University of Reading (freely downloadable from
http://www.reading.ac.uk/web/files/its/AccessInter2010.pdf).
6. McDonald, M. Access 2010: The Missing Manual, 2010, O’Reilly
Media (freely downloadable from http://it-ebooks.info/book/104/).
7. Murrey, C. & all. Oracle SQL Developer User’s Guide, Release 3.2,
2012, Oracle Corp. (freely downloadable from
http://docs.oracle.com/cd/E35137_01/appdev.32/e35117.pdf).
8. Lorentz, D. & all. Oracle Database SQL Language Reference 11g,
Release 2, 2013, Oracle Corp. (freely downloadable from
http://docs.oracle.com/cd/E11882_01/server.112/e41084.pdf).
9. Moore, S; Belden, E. Oracle Database PL/SQL Language Reference,
11g Release 2, 2013, Oracle Corp. (freely downloadable from
http://docs.oracle.com/cd/E11882_01/appdev.112/e25519.pdf).
10. Molina, H.G; Ullman, J.D; Widom, J. Database Systems: The Com-
plete Book, 2nd
Edition, 2013, Pearson New International Edition.
11. Mancas, C. Theoretical Foundations of the Relational Data Model,
2007, Ovidius University Press (in Romanian).
12. Mancas, C. Programming in SQL ANSI-92 with examples for MSJet
4.2, 2002, Ovidius University Press (in Romanian).
395
Appendix 1: DB Project Example
UNIVERSITY “POLITEHNICA” OF BUCHAREST
FACULTY OF ENGINEERING IN FOREIGN LANGUAGES
COMPUTERS AND INFORMATION TECHNOLOGY ENGLISH STREAM
Databases Project
Design, implementation, and
usage of a Library db
Project coordinator Student:
Assoc. Prof. Dr. Christian Mancas
Bucharest 201x
396
Table of Contents
1. Business Analysis
1.0 Description of the sub-universe of discourse
1.1 Entity-Relationship Diagrams
1.2 Associated Restrictions List
2. Mathematical Scheme
2.0 Initial Mathematical Scheme
2.1 First refinement algorithm: Sets, Functions, and Constraints
Design Assistance
2.1.1 Sets
2.1.2 Functions
2.1.3 Constraints
2.2 Second refinement algorithm: Keys Discovery Assistance
2.3 Third refinement algorithm: E-RD Cycles Analysis
2.4 Final Mathematical Scheme
3. Relational Scheme and Associated Non-relational Constraints List
3.1 Relational db scheme
3.2 Non-relational constraints list
4. Database Implementation
4.0 Technology choice
4.1 Access db
4.2 Oracle db
5. Non-relational Constraints Implementation
5.1 Access Solutions
5.2 Oracle Solutions
6. Database Usage
6.1 Access Queries and Reports
6.2 Oracle Views and Stored Procedures
Conclusion
References
397
1. Business Analysis
1.0 Description of the sub-universe of discourse The db should store data on books (title, writing year, first author, co-au-
thors, and their order), people (authors and/or library subscribers) e-mail
addresses and first and last names, as well as books copies borrows by
subscribers (borrow, due, and actual return dates).
Books are published by publishing houses, possibly in several editions
even by same publishers (in different years). Each edition may contain se-
veral volumes, each having a number, a title, a price, and an ISBN code.
Volumes may contain several books.
The library owns several copies (uniquely identified by an inventory
code) of any volume and may lend several copies for any borrow; not all
borrowed copies should be returned at a same date; maximum lending pe-
riod is 300 days.
Last names, books titles, first authors, publisher names, editions publi-
shers, first books, and titles, as well as volumes numbers and prices, co-
pies inventory codes, borrows subscribers, dates, and due return dates are
compulsory; for subscribers, first names and e-mail addresses are compul-
sory too.
People are uniquely identified by their first and last names, plus e-mail
address, books by their first author, title, and writing year, publishers by
their names, editions by their first book, publisher, title, and year, vo-
lumes by corresponding edition and volume number.
There may be at most:
- 1,000,000 persons, books, and editions,
- 2,000,000 co-authoring and volumes,
- 10,000 publishers,
- 4,000,000 book editions,
- 32,000,000 copies,
- 100,000,000,000 borrows, and
- 1,000,000,000,000 borrows of interest.
398
1.1 Entity-Relationship Diagrams
Fname LName e-mail
PERSONS
BTitle BOOKS BYear
PERSONS CO-AUTHORS BOOKS
PosInList
PUBLISHERS PubName
ETitle EDITIONS EYear
ISBN Title
Number VOLUMES Price
BOOKS VOLUMES_CONTENTS VOLUMES
BookPos
InvNo COPIES
399
BORROWS BorrowDate
COPIES BORROWS_LISTS BORROWS
DueReturnDate ActualReturnDate
FirstAuthor
PERSONS BOOKS
Subscriber CO-AUTHORS
BORROWS VOLUMES_CONTENTS
FirstBook
BORROWS_LISTS Volume VOLUMES
Publisher Edition
COPIES PUBLISHERS EDITIONS
Figure A1.1 Structural E-RD
1.2 Associated Restrictions List 1. PERSONS (The set of books (co-)authors and subscribers of inte-
rest to the library.)
a. Cardinality: max(card(PERSONS)) = 1,000,000 (RP0)
b. Data ranges:
FName: ASCII(128) (RP1)
LName: ASCII(64) (RP2)
e-mail: ASCII(255) (RP3)
c. Compulsory data: LName (RP4)
d. Unicity: e-mail FName LName (there may not
be two persons having same first and last name,
as well as same e-mail address) (RP5)
400
e. Other types restrictions:
FName and e-mail should be compulsory for subscribers(RP6)
2. BOOKS (The set of written works of interest to the library.)
a. Cardinality: max(card(BOOKS)) = 1,000,000 (RB0)
b. Data ranges:
BTitle: ASCII(255) (RB1)
BYear: [-2500, current year] (RB2)
c. Compulsory data: BTitle, FirstAuthor (RB3)
d. Unicity: FirstAuthor BTitle BYear (no author writes
two books with a same title in a same year) (RB4)
3. CO-AUTHORS = (PERSONS, BOOKS) (The set of pairs
<b, p> storing the fact that book b was (also) written by
person p.)
a. Cardinality: max(card(CO-AUTHORS)) = 2,000,000 (RCA0)
b. Data ranges: PosInList: [2, 16] (p’s position in b’s
co-authors list) (RCA1)
c. Compulsory data: Person, Book, PosInList (RCA2)
d. Unicity: Person Book (no author should appear more
than once in any book co-authors list) (RCA3)
4. PUBLISHERS (The set of publishers of interest to the library.)
a. Cardinality: max(card(PUBLISHERS)) = 10,000 (RPB0)
b. Data ranges: PubName: ASCII(128) (RPB1)
c. Compulsory data: PubName (RPB2)
d. Unicity: PubName (there may not be two publishers
having same name) (RPB3)
5. EDITIONS (The set of books editions of interest to the library.)
a. Cardinality: max(card(EDITIONS)) = 1,000,000 (RE0)
b. Data ranges:
ETitle: ASCII(255) (RE1)
EYear: [-2500, current year] (RE2)
c. Compulsory data: Publisher, FirstBook, ETitle (RE3)
401
d. Unicity: Publisher FirstBook EYear (no publis-
her publishes more than one edition with any given
book in any given year) (RE4)
6. VOLUMES (The set of editions volumes of interest to the library.)
a. Cardinality: max(card(VOLUMES)) = 2,000,000 (RV0)
b. Data ranges:
ISBN: ASCII(16) (RV1)
VTitle: ASCII(255) (RV2)
VNo: [1, 255] (RV3)
VPrice: CURRENCY(8) (RV4)
c. Compulsory data: Edition, VNo, VPrice (RV5)
d. Unicity: ISBN (by definition, ISBNs are unique for any
volume) (RV6)
7. VOLUMES_CONTENTS = (BOOKS, VOLUMES) (The set of
pairs <b, v> storing the fact that book b is (also) included in
volume v.)
a. Cardinality: max(card(VOLUMES_ CONTENTS)) =
4,000,000 (RVC0)
b. Data ranges: BookPos: [1, 16] (b’s position in v’s table
of contents) (RVC1)
c. Compulsory data: Book, Volume, BookPos (RVC2)
d. Unicity: Volume Book (no book should be included
more than once in any volume) (RVC3)
e. Other types restrictions:
For any edition, its first book should be the first one
published in its first volume. (RVC4)
No edition may contain same book more than once. (RVC5)
8. COPIES (The set of editions volumes copies that the
library possessed.)
a. Cardinality: max(card(COPIES)) = 32,000,000 (RC0)
b. Data ranges: InvNo: ASCII(32) (RC1)
c. Compulsory data: InvNo, Volume (RC2)
402
d. Unicity: InvNo: (by definition, inventory numbers are
unique for any copy) (RC3)
9. BORROWS (The set of editions volumes copies borrows
by subscribers.)
a. Cardinality: max(card(BORROWS)) =
100,000,000,000 (RBR0)
b. Data ranges: BorrowDate: [6/1/2011, SysDate()]
(assuming, for example, that first borrow date of
interest is June 1st, 2011) (RBR1)
c. Compulsory data: BorrowDate, Subscriber (RBR2)
d. Unicity: BorrowDate Subscriber (no subscriber may
simultaneously borrow several times) (RBR3)
10. BORROWS_LISTS = (BORROWS, COPIES) (The set of
pairs <b, c> storing the fact that volume copy c was
(also) borrowed in borrow b.)
a. Cardinality: max(card(BORROWS_ LISTS)) =
1,000,000,000,000 (RBL0)
b. Data ranges:
DueReturnDate: [6/1/2011, SysDate() + 300] (assuming,
for example, that maximum borrow period is 300 days)(RBL1)
ActualReturnDate: [6/1/2011, SysDate()] (RBL2)
c. Compulsory data: Borrow, Copy, DueReturnDate (RBL3)
d. Unicity: Borrow Copy (no copy may be simultaneously
borrowed more than once) (RBL4)
e. Other types restrictions:
No copy may be borrowed less than 0 days or more
than 300 days. (RBL5)
No copy may be simultaneously borrowed to more than
one subscriber. (RBL6)
No copy may be returned before it was borrowed and
after 100 years since corresponding borrow date. (RBL7)
403
2. Mathematical Scheme
2.0 Initial Mathematical Scheme By applying the algorithm for translating E-RDDs and restriction lists to
mathematical schemes, the following initial scheme results:
PERSONS
x NAT(6), total
FName ASCII(128)
LName ASCII(64), total
e-mail ASCII(255)
CP5: e-mail FName LName key
BOOKS
x NAT(6), total
BTitle ASCII(255), total
BYear [-2500, Year(SysDate())]
FirstAuthor : BOOKS PERSONS, total
CB4: FirstAuthor BTitle BYear key
CO-AUTHORS = (PERSONS, BOOKS)
x NAT(7), total
PosInList [2, 16], total
PUBLISHERS
x NAT(4), total
PubName ASCII(128), total
EDITIONS
x NAT(6), total
ETitle ASCII(255), total
EYear [-2500, Year(SysDate())]
Publisher : EDITIONS PUBLISHERS, total
FirstBook : EDITIONS BOOKS, total
CE4: Publisher FirstBook EYear key
404
VOLUMES
x NAT(7), total
ISBN ASCII(16)
VTitle ASCII(255)
VNo [1, 255], total
VPrice CURRENCY(8), total
Edition : VOLUMES EDITIONS, total
VOLUMES_CONTENTS = (BOOKS, VOLUMES)
x NAT(7), total
BookPos [1, 16], total
COPIES
x NAT(8), total
InvNo ASCII(32), total
Volume : COPIES VOLUMES, total
BORROWS
x NAT(11), total
BorrowDate [6/1/2011, SysDate()], total
Subscriber : BORROWS PERSONS, total
CBR3: BorrowDate Subscriber key
BORROWS_LISTS = (BORROWS, COPIES)
x NAT(12), total
DueReturnDate [6/1/2011, SysDate() + 300], total
ActualReturnDate [6/1/2011, SysDate()]
CBL4: Borrow Copy key
2.1 First refinement algorithm: Sets, Functions, and
Constraints Design Assistance
Here are the results of applying the design assistance algorithm for:
2.1.1 Sets
a.1 No set is semantically overloaded (so no structural refinements are ne-
eded from this point of view). Please review chapter 2, where this sub-
universe modeling is discussed too contrastively, as compared to several
poorer solutions!
405
a.2 No set is a subset of another set (so no inclusion constraints need to be
added to the scheme).
a.3 No association-type set has arity greater than two (so none has to be
replaced by its equivalent entity-type set and explicit structural keys and
functions corresponding to its canonical Cartesian projections).
a.4 All binary associations are non-functional:
- CO-AUTHORS is not functional, because there are persons that
wrote several books, as well as books that were written by several
persons.
- VOLUMES_CONTENTS is not functional, because there are vo-
lumes that contain several books, as well as books that span across
several volumes.
- BORROWS_LISTS is not functional, because there are borrows in-
cluding several copies, as well as copies borrowed several times.
a.5 No association-type object set is ill-defined:
- CO-AUTHORS is well defined according to restriction RCA3;
- VOLUMES_CONTENTS is well defined according to restriction
RVC3;
- BORROWS_LISTS is well defined according to restriction RBL4.
a.6 No association is homogeneous (so that we need not investigate refle-
xivity, irreflexivity, symmetry, anti-symmetry, etc.).
2.1.2 Functions
b.1 All functions are well defined:
- people (be them authors or subscribers) have only one e-mail address (of
interest to the library), first, and last name;
- books, editions, and volumes have only one respective title;
- books and editions have only one respective year ();
406
- co-authors and volumes contents entries have only one associated res-
pective position;
- publishers have only one name;
- volumes have at most one ISBN value and price, as well as one number;
- copies have only one inventory number;
- borrows have only one associated calendar date;
- borrows lists entries have only one due and actual return dates;
- books have only one first author;
- editions have only one publisher and first book;
- volumes belong to only one edition;
- copies are instances of only one volume;
- finally, borrows are made by only one subscriber.
b.2 No other function (except for the unique ones) is one-to-one:
- FName : there may be several persons having same first name;
- LName : there may be several persons having same last name;
- e-mail : there may be several persons having same e-mail address (for
example children and parents, spouses, etc.);
- BTitle: there may be several books having same title (even wrote by
same authors);
- FirstAuthor: there may be several books having a same first author;
- PosInList: there may be several authors in the same co-authors list
position (in different co-authors lists)
- ETitle: there may be several editions having same title (even published
by same publishers);
407
- EYear: there may be several editions published in a same year (even
from a same publisher);
- Publisher: there may be several editions published by a same publisher;
- FirstBook: there may be several editions having same first book (even
from a same publisher);
- VTitle: there may be several volumes having same title (in different edi-
tions);
- VNo: there may be several volumes having same number (in different
editions);
- VPrice: there may be several volumes having same price (even in same
editions);
- Edition: there may be several volumes for a same edition;
- BookPos: there may be several books having same position within vo-
lumes (in different editions);
- Volume: there may be several copies of a same volume;
- BorrowDate: there may be several borrows in a same day (by different
subscribers);
- DueReturnDate: there may be several borrowed books having same due
return day (even for a same borrow);
- ActualReturnDate: there may be several borrowed books having same
actual return day (even for a same borrow).
On the contrary, all those declared as one-to-one are really one-to-one:
- PubName: there may not be two publishers having same name;
- ISBN: there may not be two volumes having same ISBN;
- InvNo: there may not be two copies having same inventory number.
408
b.3 No function is onto, except for Edition:
- Edition is onto, because for any edition there should be at least
one corresponding volume. Consequently, the following constraint
has to be added to this db scheme:
Edition: VOLUMES EDITIONS, total, onto
- Obviously, FName, LastName, e-mail, BTitle, PubName, ETitle,
ISBN, VTitle, and InvNo are not, as not all possible ASCII charac-
ter combinations (of maximum 255/128/64/32/16 characters)
should be first or last person, publisher, or ISBN actual values;
- just as no calendar dates (between June 1st 2011 and up to today or
next year) or years (between -2500 and the current one) should (so
BYear, EYear, BorrowDate, DueReturnDate, and ActualReturn-
Date are not onto either);
- just like no naturals (between 1/2 and 16/255) or rationals (betwe-
en 0 and 99,999,999.99) should (so PosInList, VNo, VPrice, and
BookPos are not onto either).
- FirstAuthor is not either, as not only that not all subscribers are
authors too (in fact, most of them are not), but not even all authors
are first authors.
- Publisher and FirstBook are not either, as it is not compulsory that
all possible publishers/books be editing (to the current db instance
knowledge) / edited, respectively.
- Volume is not either, as there might be volumes (in the current db
instance) for which there are no copies.
- Subscriber is not either, as not all known (to the db instance)
persons should have borrowed books from the library (e.g. Homer,
Shakespeare, etc.).
b.4 There are no bijective functions.
b.5 There are no auto-functions (so that we need not investigate reflexi-
vity, irreflexivity, symmetry, anti-symmetry, etc.).
b.6 There are no canonical surjections (representative systems).
409
b.7 There are no object sets having no totally defined functions.
2.1.3 Constraints
c.1 There are six not formalized constraints; here are their corresponding
formalizations:
- RP6: FName and e-mail should be compulsory for subscribers.
CP6: (bBORROWS)(FName(Subscriber(b)) NULLS
e-mail(Subscriber(b)) NULLS)
- RVC4: For any edition, its first book should be the first one published in
its first volume.
CVC4: (eEDITIONS)(vcVOLUMES_CONTENTS)
(e = Edition(Volume(vc)) FirstBook(e) = Book(vc) BookPos(vc) = 1)
- RVC5: No edition may contain same book more than once.
CVC5: (eEDITIONS)(vc,vc’VOLUMES_CONTENTS)
(Edition(Volume(vc)) = Edition(Volume(vc’)) Book(vc) Book(vc’))
- RBL5: No copy may be borrowed less than 0 days or more than 300
days.
CBL5: (blBORROWS_LISTS)
(0 DueReturnDate(bl) – BorrowDate(Borrow(bl)) 300)
- RBL6: No copy may be simultaneously borrowed to more than one sub-
scriber.
CBL6: (bl, bl’BORROWS_LISTS) (Copy(bl) = Copy(bl’))
(ActualReturnDate(bl’) NULLS BorrowDate(Borrow(bl)
ActualReturnDate(bl’) ActualReturnDate(bl) NULLS
BorrowDate(Borrow(bl’) ActualReturnDate(bl))
- RBL7: No copy may be returned before it was borrowed and after 100
years since corresponding borrow date.
CBL7: (blBORROWS_LISTS)
(0 ActualReturnDate(bl) – BorrowDate(Borrow(bl)) 36,500)
c.2 There are no other constraints that apply in this sub-universe too, but
are missing from this model.
410
2.2 Second refinement algorithm: Keys Discovery
Assistance
Applying the assistance algorithm for keys discovery yields the follow-
ing:
PERSONS and BOOKS
n = 3, as there are three not one-to-one mappings defined on both of them
(FName, LName, and e-mail for PERSONS, and BTitle, BYear,
and FirstAuthor for BOOKS), all being prime in this context and
forming a (semantic) key for each of these two object sets (accord-
ing to CP5 and CB4, respectively) no other (semantic) keys
may exist for either of these sets.
PUBLISHERS
n = 0 no other semantic key exist.
COPIES
n = 1 (nothing to do, as according to b.2 above, Volume is not one-to-one)
no other semantic key exist.
BORROWS
n = 2 (nothing to do, as according to CBR3 above, BorrowDate Subscri-
ber is minimally one-to-one) no other semantic key exist.
CO-AUTHORS
n = 3, as there are three not one-to-one mappings defined on it, all of them
being prime: the two canonical Cartesian projections Author and
Book, plus PosInList. Obviously, K = {Author Book}, as CO-
AUTHORS is a well-defined binary association (see restriction
RCA3).
i = 2:
- Author PosInList key? No, because several authors (and even same
ones) may occupy a same position in different co-author lists.
- Book PosInList key? YES, because in any position of any co-author
list only one co-author may appear.
Consequently, K = {Author Book, Book PosInList}.
411
EDITIONS
n = 4, as there are four not one-to-one mappings defined on it, all being
prime: ETitle, Publisher, FirstBook, and EYear. According to
CE4, K = {Publisher FirstBook EYear}, as this product is mi-
nimally one-to-one.
i = 2
- ETitle Publisher key? No, because even a same publisher may publish
several editions, even of a same book (although not necessarily),
having same title.
- ETitle FirstBook key? No, because even a same publisher may publish
several editions, even of a same book (although not necessarily),
having same title and first book.
- ETitle EYear key? No, because even a same publisher may publish se-
veral editions (of different books, generally) in a same year.
i = 3
- ETitle Publisher FirstBook key? No, because even a same publisher
may publish several editions, even of a same book (although not
necessarily), having same title and same first book (in different
years).
- ETitle FirstBook EYear key? No, because even a same publisher
may publish several editions (of different books) having same first
book, in a same year.
- ETitle EYear Publisher key? No, because even a same publisher may
publish several editions (of different books, generally) in a same
year, having same title.
Consequently, EDITIONS does not have any other (semantic) key.
VOLUMES
n = 3, as there are four not one-to-one mappings defined on it: VPrice,
VTitle, VNo, and Edition, but, obviously, VPrice is not prime: pri-
ces of volumes might never contribute to their unique
identification in this context. According to RV4, K = {ISBN}.
i = 2
412
- VTitle VNo key? No, because even a same publisher may publish se-
veral editions including volumes having same title and number.
- VTitle Edition key? YES, because no edition may contain several
volumes having same title.
- VNo Edition key? YES, because no edition may contain several vo-
lumes having same number.
Consequently, K = {ISBN, VTitle Edition, VNo Edition}.
VOLUMES_CONTENTS
n = 3, as there are three not one-to-one mappings defined on it, all of them
being prime: the two canonical Cartesian projections Volume and
Book, plus BookPos. Obviously, K = {Volume Book}, as VO-
LUMES_CONTENTS is a well-defined binary association (see res-
triction RVC3).
i = 2:
- Book BookPos key? No, because same book may occupy a same posi-
tion in different volumes (of different editions).
- Volume BookPos key? YES, because in any position of any volume
only one book may appear.
Consequently, K = {Volume Book, Volume BookPos}.
BORROWS_LISTS
n = 4, as there are four not one-to-one mappings defined on it, all of them
being prime: the two canonical Cartesian projections Copy and
Borrow, plus DueReturnDate and ActualReturnDate. Obviously,
K = {Copy Borrow}, as BORROWS_LISTS is a well-defined bi-
nary association (see restriction RBL4).
i = 2:
- Borrow DueReturnDate key? No, because there may be for a same
borrow several books that need to be returned at a same date.
- Borrow ActualReturnDate key? No, because there may be for a same
borrow several books that are returned at a same date.
413
- Copy DueReturnDate key? No, because, for example, a same copy
may be borrowed several times within a same day for only some
hours (i.e 0 days).55
- Copy ActualReturnDate key? No, because, for example, a same copy
may be borrowed several times within a same day for only some
hours (i.e 0 days) and actually being returned the same day at least
twice.56
- DueReturnDate ActualReturnDate key? No, because there may be, e-
ven for a same borrow, several books that are both returned at a
same date and due to be returned at a same date.
i = 3:
- Borrow DueReturnDate ActualReturnDate key? No, because there
may be for a same borrow several books that need to be returned
at a same date and are returned at a same date.
- Copy DueReturnDate ActualReturnDate key? No, because, for
example, a same copy may be borrowed several times within a
same day for only some hours (i.e 0 days) and actually being re-
turned the same day at least twice.
Consequently, BORROWS_LISTS does not have any other (semantic) key.
2.3 Third refinement algorithm: E-RD Cycles Analysis By applying the initial step of the algorithm for E-RD cycles analysis, the
following 6 cycles are discovered in the structural E-RD shown in figure
A.1.1, out of which 3 are of the commutative type and other 3 are of the
generalized commutative type (see figures A.1.2 to A.1.7 below):
55
Note that, obviously, if DueReturnDate is implemented as a time stamp (i.e. also stor-
ing the corresponding time, not only the date), then this product is a key, but obviously,
this should not happen as it does not make that much sense to also ask for a time
deadline in this context. 56
Note that, obviously, similar to DueReturnDate above, if ActualReturnDate is imple-
mented as a time stamp (i.e. also storing the corresponding time, not only the date, which
would make sense), then this product is a key.
414
2.3.1 First commutative-type cycle
FirstAuthor
PERSONS BOOKS
CO-AUTHORS
Figure A.1.2 First commutative-type cycle from A.1.1
Obviously, the corresponding commutativity question is: Coauthor ?=
FirstAuthor Book (should, for any book, any coauthor be its first au-
thor?); trivially, the answer is no (as there may be several coauthors for a
book, but there is only one first coauthor for each book, and first authors
should appear only once in the coauthors lists, on the corresponding first
position).
Dually, the corresponding anti-commutativity question is: (x CO-AU-
THORS) (Coauthor(x) ? FirstAuthor Book(x)) (should, for any book,
any coauthor be distinct than its first author?); obviously, the answer is
yes (as first authors should appear only once in the coauthors lists, on
the corresponding first position). Consequently, the following con-
straint should be added to the db scheme:
ACC1: (xCO-AUTHORS) (Coauthor(x) FirstAuthor Book(x))
2.3.2 First generalized commutative-type cycle
PERSONS BOOKS
Subscriber CO-AUTHORS
BORROWS VOLUMES_CONTENTS
BORROWS_LISTS Volume VOLUMES
COPIES
Figure A.1.3 First generalized commutative-type cycle from A.1.1
415
This cycle has three sources (CO-AUTHORS, VOLUMES_CONTENTS,
and BORROWS_ LISTS) and three destinations (PERSONS, BOOKS, and
VOLUMES).
Let us consider any elements from the three source nodes: co from CO-
AUTHORS, vc from VOLUMES_CONTENTS, and bl from BORROWS_
LISTS; the corresponding three equality questions (associated to the desti-
nation nodes) are the following:
1. Author(co) ?= Subscriber(Borrow(bl)) (are there cases when co-
authors of books should also be borrowers?)
2. Book(co) ?= Book(vc)) (are there cases when co-authored books
should also be published in edition volumes?)
3. Volume(vc) ?= Volume(Copy(bl)) (are there cases when published
in edition volumes should also have borrowed copies?).
Corresponding answers are the following:
1. No: co-authors may be borrowers too, but this is never mandatory.
2. No: on one hand, co-authored (just like any other) books may not
be published; on the other, published volumes need not to be co-
authored.
3. On one hand, no: published volumes need not even to be in the li-
brary’s possession; on the other, YES: all borrowed copies have to
have been previously published.
Consequently, at a first glance, the following constraint has to be added to
the db too:
CBL8:(blBORROWS_LISTS)(vVOLUMES)(Volume(Copy(bl)) = v).
Thinking deeper, this constraint is implied by the following stronger one:
CC4: (cCOPIES)(vVOLUMES)(Volume(c) = v) (in order for a vo-
lume copy to exist, that volume should have been published),
which is obviously equivalent to the second half of RC2: Volume should
be totally defined; as this constraint is already present in the db scheme,
no other implied constraint has to be added to it.
416
2.3.3 Second generalized commutative-type cycle
This cycle has two sources (VOLUMES_CONTENTS and BORROWS_
LISTS), as well as two destinations (PERSONS and VOLUMES).
FirstAuthor
PERSONS BOOKS
Subscriber
BORROWS VOLUMES_CONTENTS
BORROWS_LISTS Volume VOLUMES
COPIES
Figure A.1.4 Second generalized commutative-type cycle from A.1.1
Let us consider any elements from the two source nodes: vc from VO-
LUMES CONTENTS and bl from BORROWS_ LISTS; the corresponding
two equality questions (associated to the destination nodes) are the fol-
lowing:
1. FirstAuthor(Book(vc)) ?= Subscriber(Borrow(bl)) (are there cases
when first authors of books should also be borrowers?)
2. Volume(vc) ?= Volume(Copy(bl)) (are there cases when published
in edition volumes should also have borrowed copies?).
Corresponding answers are the following:
1. No: first authors may be borrowers too, but this is never mandato-
ry.
2. On one hand, no: published volumes need not even to be in the li-
brary’s possession; on the other, YES: all borrowed copies have to
have been previously published.
Consequently, as per 2.3.2 above, no additional constraint should be ad-
ded to the db scheme.
417
2.3.4 Second commutative-type cycle
BOOKS
VOLUMES_CONTENTS
FirstBook
VOLUMES
Edition
EDITIONS
Figure A.1.5 Second commutative-type cycle from A.1.1
Obviously, there is one source (VOLUMES_CONTENTS), one destination
(BOOKS), and the corresponding commutativity question is: Book ?=
FirstBook Edition Volume (should, for any volume, any included book
be its first one?); trivially, the answer is no (as there may be several books
for a volume, but there is only one such first book per volume).
Dually, the corresponding anti-commutativity question is: ( x VO-
LUMES_CONTENTS) (Book(x) ? FirstBook Edition Volume(x))
(should, for any volume, any included book be distinct from its first
one?); obviously, the answer is no, as the first book is always equal to
itself, so no supplementary constraint should be added to the db scheme in
connection with this cycle (which is an uninteresting one).
2.3.5 Third generalized commutative-type cycle
This cycle has two sources (CO-AUTHORS and BORROWS_ LISTS), as
well as two destinations (PERSONS and BOOKS).
Let us consider any elements from the two source nodes: ca from CO-
AUTHORS and bl from BORROWS_ LISTS; the corresponding two equa-
lity questions (associated to the destination nodes) are the following:
1. Author(ca) ?= Subscriber(Borrow(bl)) (are there cases when co-
authors of books should also be borrowers?)
418
PERSONS BOOKS
Subscriber CO-AUTHORS
BORROWS
FirstBook
BORROWS_LISTS Volume VOLUMES
Edition
COPIES EDITIONS
Figure A.1.6 Third generalized commutative-type cycle from A.1.1
2. Book(ca) ?= FirstBook(Edition(Volume(Copy(bl))) (are there ca-
ses when co-authored books should also have borrowed copies of
volumes of their editions?).
Corresponding answers are the following:
1. No: co-authors may be borrowers too, but this is never mandatory
(also see 2.3.2 above).
2. On one hand, no: co-authored books need not even to be in the li-
brary’s possession; on the other, no again: not all borrowed copies
have to have been co-authored (some might only have one author).
Consequently, no supplementary constraint should be added to the db
scheme in connection with this cycle (which is an uninteresting one).
2.3.6 Third commutative-type cycle
Obviously, there is one source (BORROWS_LISTS), one destination
(PERSONS), and the corresponding commutativity question is: Subscri-
ber Borrow ?= FirstAuthor FirstBook Edition Volume Copy
(should subscribers borrowing copies be first authors of first books of the
editions to which that volume copies belongs?); trivially, the answer is no
(as any subscriber may borrow books without being either the first or any
other co-other of the corresponding book).
419
FirstAuthor
PERSONS BOOKS
Subscriber
BORROWS
FirstBook
BORROWS_LISTS Volume VOLUMES
Edition
COPIES EDITIONS
Figure A.1.7 Third commutative-type cycle from A.1.1
Dually, the corresponding anti-commutativity question is: (x BOR-
ROWS_LISTS) (Subscriber Borrow(x) ? FirstAuthor FirstBook Edi-
tion Volume Copy(x)) (should subscribers borrowing copies never be
first authors of first books of the editions to which that volume copies be-
longs?); obviously, the answer is no (as any first author of a book may
borrow that book too), so no supplementary constraint should be added to
the db scheme in connection with this cycle (which is an uninteresting
one too).
2.4 Final Mathematical Scheme By merging all above refinements with the initial scheme from section 2.0
above, the following final mathematical scheme results:
PERSONS
x NAT(6), total
FName ASCII(128)
LName ASCII(64), total
e-mail ASCII(255)
CP5: e-mail FName LName key
BOOKS
x NAT(6), total
BTitle ASCII(255), total
420
BYear [-2500, Year(SysDate())]
FirstAuthor : BOOKS PERSONS, total
CB4: FirstAuthor BTitle BYear key
CO-AUTHORS = (PERSONS, BOOKS)
x NAT(7), total
PosInList [2, 16], total
CAK2: Book PosInList key
PUBLISHERS
x NAT(4), total
PubName ASCII(128), total
EDITIONS
x NAT(6), total
ETitle ASCII(255), total
EYear [-2500, Year(SysDate())]
Publisher : EDITIONS PUBLISHERS, total
FirstBook : EDITIONS BOOKS, total
CE4: Publisher FirstBook EYear key
VOLUMES
x NAT(7), total
ISBN ASCII(16)
VTitle ASCII(255)
VNo [1, 255], total
VPrice CURRENCY(8), total
Edition : VOLUMES EDITIONS, total, onto
VK1: VTitle Edition key
VK2: VNo Edition key
VOLUMES_CONTENTS = (BOOKS, VOLUMES)
x NAT(7), total
BookPos [1, 16], total
VCK2: Volume BookPos key
COPIES
x NAT(8), total
InvNo ASCII(32), total
421
Volume : COPIES VOLUMES, total
BORROWS
x NAT(11), total
BorrowDate [6/1/2011, SysDate()], total
Subscriber : BORROWS PERSONS, total
CBR3: BorrowDate Subscriber key
BORROWS_LISTS = (BORROWS, COPIES)
x NAT(12), total
DueReturnDate [6/1/2011, SysDate() + 300], total
ActualReturnDate [6/1/2011, SysDate()]
CBL4: Borrow Copy key
CP6: (bBORROWS)(FName(Subscriber(b)) NULLS
e-mail(Subscriber(b)) NULLS)
CVC4: (eEDITIONS)(vcVOLUMES_CONTENTS)
(e = Edition(Volume(vc)) FirstBook(e) = Book(vc) BookPos(vc) = 1)
CVC5: (eEDITIONS)(vc,vc’VOLUMES_CONTENTS)
(Edition(Volume(vc)) = Edition(Volume(vc’)) Book(vc) Book(vc’))
CBL5: (blBORROWS_LISTS)
(0 DueReturnDate(bl) – BorrowDate(Borrow(bl)) 300)
CBL6: (bl, bl’BORROWS_LISTS) (Copy(bl) = Copy(bl’))
(ActualReturnDate(bl’) NULLS BorrowDate(Borrow(bl)
ActualReturnDate(bl’) ActualReturnDate(bl) NULLS
BorrowDate(Borrow(bl’) ActualReturnDate(bl))
CBL7: (blBORROWS_LISTS)
(0 ActualReturnDate(bl) – BorrowDate(Borrow(bl)) 36,500)
ACC1: (xCO-AUTHORS) (Coauthor(x) FirstAuthor Book(x))
Note that, as compared to the initial one, 11 more constraints (out of
which there are four keys and seven non-relational ones) have been added
422
to this final one, namely: CAK2; Edition : VOLUMES EDITIONS, on-
to; VK1; VK2; VCK2; CVC4; CVC5; CBL5; CBL6; CBL7; and ACC1.
3. Relational Scheme and Associated Non-relational
Constraints List
3.0 Relational db scheme
By applying the algorithm for translating mathematical schemes into rela-
tional ones and non-relational constraint lists, the following output is ob-
tained (with an example instance having at least two tuples per table):
PERSONS (x, e-mail FName LName)
x FName LName e-mail
NAT(6) ASCII(128) ASCII(64) ASCII(255)
NOT
NULL
NOT NULL
1 Homer
2 William Shakespeare
3 Peter Buneman opb @ inf.ed.ac.uk
4 Serge Abiteboul
5 Dan Suciu [email protected]
BOOKS (x, FirstAuthor BTitle BYear)
x FirstAuthor BTitle BYear
NAT(6) Im(PERSONS.x) ASCII(255) [-2500,
Year(SysDate())]
NOT
NULL
NOT NULL NOT NULL
1 1 Odyssey -700
2 2 As You Like It 1600
3 4 Data on the Web: From
Relations to Semi-
structured Data and
XML
1999
423
CO-AUTHORS (x, Person Book, Book PosInList)
x Book Person PosInList
NAT(7) Im(BOOKS.x) Im(PERSONS.x) [2, 16]
NOT NULL NOT NULL NOT NULL NOT NULL
1 3 3 2
2 3 5 3
PUBLISHERS (x, PubName)
x PubName
NAT(4) ASCII(128)
NOT NULL NOT NULL
1 Apple Academic Press
2 Springer Verlag
3 Morgan Kaufmann
4 Penguin Books
5 Washington Square Press
EDITIONS (x, Publisher FirstBook EYear)
x Publisher FirstBook ETitle EYear
NAT(6
)
Im(PUBLISHERS.
x)
Im(BOOKS.
x)
ASCII(25
5)
[-2500,
Year(SysDate()
)]
NOT
NULL
NOT NULL NOT NULL NOT
NULL
1 4 1 The
Odyssey
translated
by Robert
Fagles
2012
2 5 2 As You
Like It
2011
3 3 3 Data on
the Web:
From
Relations
1999
424
to Semi-
structured
Data and
XML
VOLUMES (x, ISBN, Edition VNo, Edition VTitle)
x Edition VNo VTitle ISBN Price
NAT(7) Im(EDI-
TIONS.x)
[1,
255]
ASCII(255) ASCII(16) CUR-
RENCY(8)
NOT
NULL
NOT
NULL
NOT
NULL
NOT NULL
1 1 1 0-670-
82162-4
$12.95
2 2 1 978-
1613821114
$9.99
3 3 1 978-
1558606227
$74.95
VOLUMES_CONTENTS (x, Volume Book, Volume BookPos)
x Volume Book BookPos
NAT(7) Im(VOLUMES.x) Im(BOOKS.x) [1, 255]
NOT NULL NOT NULL NOT NULL NOT NULL
1 1 1 1
2 2 2 1
3 3 3 1
425
BORROWS (x, BorrowDate Subscriber)
x Subscriber BorrowDate
NAT(11) Im(PERSONS.x) [6/1/2011, SysDate()]
NOT NULL NOT NULL NOT NULL
1 5 10/29/2012
COPIES (x, InvNo)
x InvNo Volume
NAT(8) ASCII(32) Im(VOLUMES.x)
NOT NULL NOT NULL NOT NULL
1 H-O-1 1
2 H-O-2 1
3 S-AYLI-1 2
4 ABS-DW-1 3
5 ABS-DW-2 3
BORROWS_LISTS (x, Borrow Copy)
x Borrow Copy DueRe-
turnDate
ActualRe-
turnDate
NAT(12) Im(BORROWS.x) Im(COPIES.x) [6/1/2011,
SysDate()
+ 300]
[6/1/2011,
SysDate()]
NOT
NULL
NOT NULL NOT NULL NOT
NULL
1 1 1 11/29/2012 11/23/2012
2 1 3 12/29/2012
3.1 Non-relational constraints list The following 8 constraints are non-relational:
Edition : VOLUMES EDITIONS, onto;
CP6:
(bBORROWS)(FName(Subscriber(b)) NULLS
e-mail(Subscriber(b)) NULLS)
426
CVC4:
(eEDITIONS)(vcVOLUMES_CONTENTS)
(e = Edition(Volume(vc)) FirstBook(e) = Book(vc) BookPos(vc) = 1)
CVC5:
(eEDITIONS)(vc,vc’VOLUMES_CONTENTS)
(Edition(Volume(vc)) = Edition(Volume(vc’)) Book(vc) Book(vc’))
CBL5:
(blBORROWS_LISTS)
(0 DueReturnDate(bl) – BorrowDate(Borrow(bl)) 300)
CBL6:
(bl, bl’BORROWS_LISTS) (Copy(bl) = Copy(bl’))
(ActualReturnDate(bl’) NULLS BorrowDate(Borrow(bl)
ActualReturnDate(bl’) ActualReturnDate(bl) NULLS
BorrowDate(Borrow(bl’) ActualReturnDate(bl))
CBL7:
(blBORROWS_LISTS)
(0 ActualReturnDate(bl) – BorrowDate(Borrow(bl)) 36,500)
ACC1:
(xCO-AUTHORS) (Coauthor(x) FirstAuthor Book(x)).
4. Database Implementation
4.0 Technology choice
I have chosen MS Access because of the following reasons:
- being the only RDBMS installed on the faculty labs PCs, we did
our DB labs in Access;
427
- I have a copy of MS Office 2010, including Access 2010, installed
on my PC;
- Access is a fine choice for small and medium (up to 2 GB) dbs;
- any RDBMS is just a tool: you should exploit it at maximum, do
not use its non-sense facilities, and concentrate on the correctness
and optimality of your solution.
I have chosen Oracle Database because of the following reasons:
- being the favorite RDBMS used in western universities DB labs,
the DB Labs Notes by Prof. Christian Mancas and Drd. Alina Di-
cu also include Oracle solutions, in parallel with the Access ones;
- I have a freely downloadable copy of Oracle Xy installed on my
PC;
- Oracle is a fine choice for medium and large dbs;
- any RDBMS is just a tool: you should exploit it at maximum, do
not use its non-sense facilities, and concentrate on the correctness
and optimality of your solution.
4.1 Access db By applying the algorithm for translating rdb schemes into Access 2010
dbs, the following db was obtained (note that a Boolean Author? column
was added to PERSONS, for easing both non-relational constraint en-
forcement and users interaction with the db):
4.1.0 DDL statements for creating and populating the db CREATE TABLE PERSONS (x COUNTER PRIMARY KEY,
FName VARCHAR(128), LName VARCHAR(64) NOT NULL,
[e-mail] VARCHAR(255), [Author?] BIT,
CONSTRAINT PKey UNIQUE ([e-mail], LName, FName));
CREATE TABLE BOOKS (x COUNTER PRIMARY KEY,
FirstAuthor LONG NOT NULL, BTitle VARCHAR(255) NOT NULL,
BYear INT,
CONSTRAINT fkFA FOREIGN KEY (FirstAuthor) REFERENCES
PERSONS,
CONSTRAINT BKey UNIQUE (BTitle, FirstAuthor, BYear));
CREATE TABLE [CO-AUTHORS] (x COUNTER PRIMARY KEY,
Person LONG NOT NULL, Book LONG NOT NULL,
PosInList BYTE NOT NULL,
428
CONSTRAINT fkP FOREIGN KEY (Person) REFERENCES
PERSONS,
CONSTRAINT fkB FOREIGN KEY (Book) REFERENCES
BOOKS,
CONSTRAINT CAAKey UNIQUE (Book, Person),
CONSTRAINT CAPKey UNIQUE (Book, PosInList));
CREATE TABLE PUBLISHERS (x COUNTER PRIMARY KEY,
PubName VARCHAR(128) NOT NULL UNIQUE);
CREATE TABLE EDITIONS (x COUNTER PRIMARY KEY,
Publisher LONG NOT NULL, FirstBook LONG NOT NULL,
ETitle VARCHAR(255) NOT NULL, EYear INT,
CONSTRAINT fkPub FOREIGN KEY (Publisher) REFERENCES
PUBLISHERS,
CONSTRAINT fkBk FOREIGN KEY (FirstBook) REFERENCES
BOOKS,
CONSTRAINT EKey UNIQUE (FirstBook, Publisher, EYear));
CREATE TABLE VOLUMES (x COUNTER PRIMARY KEY,
Edition LONG NOT NULL, VNo BYTE NOT NULL,
VTitle VARCHAR(255), ISBN VARCHAR(16) UNIQUE,
Price CURRENCY NOT NULL,
CONSTRAINT fkE FOREIGN KEY (Edition) REFERENCES
EDITIONS,
CONSTRAINT VNKey UNIQUE (Edition, VNo),
CONSTRAINT VTKey UNIQUE (Edition, VTitle));
CREATE TABLE VOLUMES_CONTENTS (x COUNTER PRIMARY KEY,
Volume LONG NOT NULL, Book LONG NOT NULL,
BookPos BYTE NOT NULL,
CONSTRAINT fkV FOREIGN KEY (Volume) REFERENCES
VOLUMES,
CONSTRAINT fkVB FOREIGN KEY (Book) REFERENCES
BOOKS,
CONSTRAINT VCBKey UNIQUE (Volume, Book),
CONSTRAINT VCPKey UNIQUE (Volume, BookPos));
CREATE TABLE BORROWS (x COUNTER PRIMARY KEY,
Subscriber LONG NOT NULL, BorrowDate DATE NOT NULL,
CONSTRAINT fkS FOREIGN KEY (Subscriber) REFERENCES
PERSONS,
CONSTRAINT BRKey UNIQUE (BorrowDate, Subscriber));
CREATE TABLE COPIES (x COUNTER PRIMARY KEY,
Volume LONG NOT NULL, InvNo VARCHAR(32) NOT NULL UNIQUE,
CONSTRAINT fkS FOREIGN KEY (Volume) REFERENCES VOLUMES);
CREATE TABLE BORROWS_LISTS (x COUNTER PRIMARY KEY,
Borrow LONG NOT NULL, Copy LONG NOT NULL,
429
DueReturnDate DATE NOT NULL, ActualReturnDate DATE,
CONSTRAINT fkBw FOREIGN KEY (Borrow) REFERENCES
BORROWS,
CONSTRAINT fkCy FOREIGN KEY (Copy) REFERENCES
COPIES,
CONSTRAINT BLKey UNIQUE (Copy, Borrow));
4.1.1 Tables Relationships
Figure A.1.8 Relationships between LibraryDB.accdb tables
4.1.2 Table Lookups SQL statements
4.1.2.1 PERSONS
Foreign keys BOOKS.FirstAuthor and CO_AUTHORS.Co-Author are
using the following query:
SELECT x, IIf(IsNull([FName]),"",[FName] & " ") & [LName] &
IIf(IsNull([e-mail]),""," " & [e-mail])
FROM PERSONS
WHERE [Author?]
ORDER BY IIf(IsNull([FName]),"",[FName] & " ") & [LName] &
IIf(IsNull([e-mail]),""," " & [e-mail]);
Foreign key BORROWS.Subscriber is using the following query:
SELECT x, [e-mail] & " " & [FName] & " " & [LName]
AS [e-mail First and Last Names]
FROM PERSONS
430
ORDER BY [e-mail] & " " & [FName] & " " & [LName];
4.1.2.2 BOOKS
Foreign keys CO_AUTHORS.Book, EDITIONS.FirstBook, and VO-
LUMES_CONTENTS.Book are using the following query:
SELECT BOOKS.x, IIf(IsNull([FName]),"",[FName] & " ") &
[LName] & ", " & [BTitle] & ", " & [BYear]
AS [FirstAuthor, Book Title, Year]
FROM PERSONS INNER JOIN BOOKS
ON PERSONS.x = BOOKS.FirstAuthor
ORDER BY IIf(IsNull([FName]),"",[FName] & " ") & [LName] &
", " & [BTitle] & ", " & [BYear];
4.1.2.3 PUBLISHERS
Foreign key EDITIONS.Publisher is using the following query:
SELECT x, PubName FROM PUBLISHERS ORDER BY PubName;
4.1.2.4 EDITIONS
Foreign key VOLUMES.Edition is using the following query:
SELECT EDITIONS.x, IIf(IsNull([FName]),"",[FName] & " ") &
[LName] & ", " & [ETitle] & IIf(IsNull([eyear]),"",", " &
[EYear]) & ", " & IIf(IsNull([Publisher]),"",[PubName]) &
", " & [BTitle]
AS [First Author, Title, Year, Publisher, First Book]
FROM PUBLISHERS RIGHT JOIN (PERSONS INNER JOIN (BOOKS
INNER JOIN EDITIONS ON BOOKS.x = EDITIONS.FirstBook)
ON PERSONS.x = BOOKS.FirstAuthor)
ON PUBLISHERS.x = EDITIONS.Publisher
ORDER BY IIf(IsNull([FName]),"",[FName] & " ") & [LName] &
", " & [ETitle] & IIf(IsNull([eyear]),"",", " & [EYear])
& ", " & IIf(IsNull([Publisher]),"",[PubName]) & ", " &
[BTitle];
4.1.2.5 VOLUMES
Foreign key COPIES.Volume is using the following query:
SELECT VOLUMES.x, IIf(IsNull([FName]),"",[FName] & " ") &
[LName] & ", " & [etitle] & ", " &
IIf(IsNull([eyear]),"",[eyear]) & ", " & [PubName] &
", v." & [VNo]
AS [First Author, EdTitle, EdYear, Publisher, VolNo],
VOLUMES.ISBN
FROM PUBLISHERS RIGHT JOIN (PERSONS RIGHT JOIN ((BOOKS
431
RIGHT JOIN EDITIONS ON BOOKS.x = EDITIONS.FirstBook)
RIGHT JOIN VOLUMES ON EDITIONS.x = VOLUMES.Edition)
ON PERSONS.x = BOOKS.FirstAuthor)
ON PUBLISHERS.x = EDITIONS.Publisher
ORDER BY IIf(IsNull([FName]),"",[FName] & " ") & [LName] &
", " & [etitle] & ", " & IIf(IsNull([eyear]),"",[eyear])
& ", " & [PubName] & ", v." & [VNo];
4.1.2.6 COPIES
Foreign key BORROWS_LISTS.Copy is using the following query:
SELECT COPIES.x, "Inv.No. " & [InvNo] & ", " &
IIf(IsNull([FName]),"",[FName] & " ") & [LName]
& ", " & [etitle] & ", " &
IIf(IsNull([eyear]),"",[eyear]) & ", " &
[PubName] & ", v." & [VNo]
AS [InvNo, First Author, EdTitle, EdYear,
Publisher, VolNo]
FROM (PUBLISHERS RIGHT JOIN (PERSONS INNER JOIN
((BOOKS INNER JOIN EDITIONS
ON BOOKS.x = EDITIONS.FirstBook) INNER JOIN
VOLUMES ON EDITIONS.x = VOLUMES.Edition)
ON PERSONS.x = BOOKS.FirstAuthor)
ON PUBLISHERS.x = EDITIONS.Publisher)
INNER JOIN COPIES ON VOLUMES.x = COPIES.Volume
ORDER BY "Inv.No. " & [InvNo] & ", " &
IIf(IsNull([FName]),"",[FName] & " ") & [LName]
& ", " & [etitle] & ", " &
IIf(IsNull([eyear]),"",[eyear]) & ", " &
[PubName] & ", v." & [VNo];
4.1.2.7 BORROWS
Foreign key BORROWS_LISTS.Borrow is using the following query:
SELECT BORROWS.x, [FName] & " " & [LName] & ", "
& [e-mail] & ", " & [BorrowDate]
AS [Subscriber, e-mail, Borrow Date]
FROM PERSONS INNER JOIN BORROWS
ON PERSONS.x = BORROWS.Subscriber
ORDER BY [FName] & " " & [LName] & ", " &
[e-mail] & ", " & [BorrowDate];
432
4.1.3 Validation rules, Comments, and Default values
4.1.3.1 Enforcing (co-)domain (range) and tuple (check) constraints
Unfortunately, Access’ SQL does not provide means for enforcing either
(co-)domain (range) or tuple (check) constraints. Fortunately, both of
them can be enforced through its GUI57
by using validation rules (and as-
sociated validation texts).
Figure A.1.9 shows the result of enforcing restriction RB2, the co-domain
of BYear : BOOKS [-2500, current year].
Figure A.1.9 Enforcing restriction RB2
Figure A.1.10 shows the result of enforcing restriction RBR1, the co-
domain of BorrowDate : BORROWS [6/1/2011, SysDate()].
Figure A.1.11 shows the result of enforcing restriction RBL1, the co-
domain of DueReturnDate : BORROWS_LISTS [6/1/2011, SysDate() +
300].
Figure A.1.12 shows the result of enforcing restriction RBL2, the co-do-
main of ActualReturnDate : BORROWS_LISTS [6/1/2011, SysDate() +
900]. 57
They can also be manipulated programmatically, through VBA, DAO, and ADO.
433
Figure A.1.10 Enforcing restriction RBR1 and adding default value to
BorrowDate
Figure A.1.11 Enforcing restriction RBL1
Figure A.1.13 shows the result of enforcing restriction RCA1, the co-
domain of PosInList : CO-AUTHORS [1, 16].
434
Figure A.1.12 Enforcing restriction RBL2
Figure A.1.13 Enforcing restriction RCA1 and adding default value to
PosInList
Figure A.1.14 shows the result of enforcing restriction RE2, the co-
domain of EYear : EDITIONS [-2500, current year].
435
Figure A.1.14 Enforcing restriction RE2 and adding EYear’s default value
Figure A.1.15 shows the result of enforcing restriction RV4, the co-
domain of VNo : VOLUMES [1, 255].
Figure A.1.15 Enforcing restriction RV3 and adding VNo’s default value
436
Figure A.1.16 shows the result of enforcing restriction RVC1, the co-
domain of BookPos : VOLUMES_CONTENTS [1, 16].
Figure A.1.16 Enforcing restriction RVC1 and adding BookPos’ default
value
There are no tuple (check) constraints in this db; figure A.1.17 shows the
properties of a table, where, through its Validation rule (and associated
text) such constraints may be enforced.
Figure A.1.17 Enforcing tuple (check) constraints
437
4.1.3.2 Adding comments
Comments describing sets (tables) semantics are stored in the tables’ Des-
cription property (see figure A.1.17). Those on functions (columns), in
the homonym one of columns (fields, see figures A.1.9 to A.1.16).
4.1.3.2 Adding default values
Access too provides a Default Value property for functions (columns,
fields), which should always be used when appropriate to spare users re-
peatedely entering same most frequent values.
Figures A.1.10 and A.1.13 to A.1.16 show the default values added to this
db scheme. No other default values are appropriate.
4.2 Oracle db
5. Non-relational constraints enforcement
5.1 Access solutions In order to enforce non-relational constraints in Access, forms have to be
created for all involved tables, in order to associate to them VBA classes
that host corresponding trigger-type event-driven methods. These forms
will then have to make part of a db application that should hide corres-
ponding db tables and only allow data manipulation through forms.
Obviously, for this db, forms are needed to this end for tables: EDI-
TIONS, BORROWS, VOLUMES_CONTENTS, BORROWS_LISTS, and
CO-AUTHORS.
5.1.1 Edition : VOLUMES EDITIONS, onto
(y EDITIONS)( x VOLUMES)(Edition(x) = y) (Any edition should
have at least one volume.)
Ontoness is very easy to enforce: in this case, for example, each time a
new edition is inserted, a corresponding (first) volume should automati-
cally be inserted too and, dually, each time deletion of the last remaining
volume of an edition is successfully committed, the corresponding edition
should be automatically deleted too.
438
Consequently, this constraint has to be enforced both in the Form_EDI-
TIONS class, by using the Form_AfterUpdate event (so that the new edi-
tion be already saved in the corresponding table) and in the Form_VO-
LUMES class, by using the Form_AfterDelConfirm event (so that the last
volume be actually deleted from the corresponding table).
Note that automatically adding a first volume for each new edition, as
well as of the first book of this first volume in its content (see constraint
CVC4 below) is also an ergonomic feature of the application.
Also note that volumes cannot be deleted unless their content is previous-
ly deleted too.58
Not only as the the first book of the first volume of any
edition is automatically added, but especially for ergonomical reasons, the
application is also automatically deleting volume contents of any deleta-
ble volume, if users agree with it.
Note too that for deleting an edition left without any volume there are two
possible solutions:
a “tougher” one, consisting in deletion of any edition having no
volumes (for which no edition key is necessary)
a “softer” one, consisting in deleting only the edition just left
without any volume (for which its id should be previously stored
in a variable, as, after successful deletion of its last volume, it can-
not be anymore retrieved from the db)
Although it is a little bit more complicated, we chose the second approach
mainly because, anyhow, in order to establish whether or not a volume is
deletable and then to also automatically delete its content, programming
of the Form_Delete method is required.
Finally, note that, for ergonomical reasons, after successful automatical
deletions and insertions of both volumes and editions, all involved forms
and combo-boxes are requeried, in order for users not having to close and
re-open them for refreshing their data sources.
58
Obviously, volume cannot be deleted either if there are copies of them.
439
Here is, first, the code of class Form_VOLUMES:
'Form_VOLUMES class
Option Compare Database
Option Explicit
Private currEdition As Long
'*****************************************
Private Sub Form_Delete(Cancel As Integer)
'*****************************************
'Initializes global variable currEdition
'that is then used by Form_AfterDelConfirm.
'Moreover, prevents deletion when there are copies of the
'volume and deletes corresponding volume content if there
'are no copies of the volume and the user agrees with it,
'in order for deletion to succeed.
On Error GoTo err_point
If vbCancel = MsgBox("Are you sure you want to delete " _
& "the current volume?", _
vbQuestion + vbOKCancel + vbDefaultButton2, _
"Please confirm or cancel your request...") Then
Cancel = True
Else
If Not IsNull(DLookup("x", "COPIES", "Volume=" & Me!x))
Then
Cancel = True
MsgBox "Request denied!", vbCritical, _
"There are copies of this volume..."
Else
If vbCancel = MsgBox("Are you sure you want to also " _
& "delete the content of this volume?", _
vbQuestion + vbOKCancel + vbDefaultButton2, _
"Please confirm or cancel your request...")
Then
Cancel = True
MsgBox "Request denied!", vbCritical, _
"To delete volumes, their content must be " _
& "deleted too..."
Else
currEdition = Me!Edition
DoCmd.RunSQL "DELETE FROM VOLUMES_CONTENTS WHERE " _
& "Volume=" & Me!x
End If
End If
End If
Exit Sub
440
err_point: MsgBox Err.Source & " -> " & Err.Description, _
vbCritical, _
"Error in method Form_VOLUMES.Form_Delete..."
Cancel = True
End Sub
'**********************************************************
Private Sub Form_BeforeDelConfirm(Cancel As Integer,
Response As Integer)
'**********************************************************
'prevents Access from displaying its standard deletion
'confirmation message
Response = acDataErrContinue
End Sub
'**************************************************
Private Sub Form_AfterDelConfirm(Status As Integer)
'**************************************************
'enforces ontoness of Edition : VOLUMES -> EDITIONS
'(immediately after deletion of a last volume of an
'edition, also deletes corresponding edition)
On Error GoTo err_point
If Status = acDeleteOK Then
If IsNull(DLookup("x", "VOLUMES", "Edition=" _
& currEdition)) Then
DoCmd.RunSQL "DELETE FROM EDITIONS WHERE x=" _
& currEdition
MsgBox "Successfully deleted corresponding edition " _
& "too!", vbInformation, _
"Deleted volume was the only one of its edition..."
On Error Resume Next
Forms!EDITIONS.Requery
Forms!VOLUMES_CONTENTS.Requery
Forms!VOLUMES_CONTENTS!Volume.Requery
End If
End If
Exit Sub
err_point: MsgBox Err.Source & " -> " & Err.Description, _
vbCritical, "Error in method Form_VOLUMES. " _
& "Form_AfterDelConfirm..."
End Sub
Secondly, here is the code of class Form_EDITIONS (obviously, not all
of it enforces this constraint, but, as during its enforcement the values of
the variables v, b, and p are needed to, corresponding code could not be
understood without their definitions and initializations):
441
'Form_EDITIONS class
Option Compare Database
Option Explicit
Dim v, b, p As Long
Dim switchPos As Boolean
'*************************
Private Sub Form_Current()
'*************************
On Error GoTo err_point
switchPos = False
Exit Sub
err_point: MsgBox Err.Source & " -> " & Err.Description, _
vbCritical, "Error in method Form_EDITIONS.Form_Current..."
End Sub
'****************************************************
Private Sub FirstBook_BeforeUpdate(Cancel As Integer)
'****************************************************
'enforces constraints CVC4 (for any edition, its first book
should be
'the first one published in its first volume) and CVC5
'(no edition should include a book more than once)
Dim w, x As Variant
On Error GoTo err_point
If Me!FirstBook <> Me!FirstBook.OldValue Then
'CVC4: first book of any edition should be the first one
' of the edition first volume
If IsNull(Me!FirstBook) Then
Cancel = True
MsgBox "Please choose a not null value for First " _
& "Book!", vbCritical, _
"First book of any edition is compulsory..."
Me!FirstBook.Undo
Else
v = DLookup("VNo", "VOLUMES", "Edition = " & Me!x & _
" AND VNo=1")
If IsNull(v) Then
insertFirstVolume
Else
'CVC5: no edition should include a book more than once
w = DLookup("x", "VOLUMES_CONTENTS", "Book=" & _
Me!FirstBook & " AND Volume IN (SELECT x " _
& "FROM VOLUMES WHERE Edition=" & Me!x & ")")
If IsNull(w) Then
442
DoCmd.RunSQL "UPDATE VOLUMES_CONTENTS SET Book=" _
& Me!FirstBook & " WHERE Volume=" & v _
& " AND BookPos=1"
Else
x = DLookup("VNo", "VOLUMES", "x=" & _
DLookup("Volume", "VOLUMES_CONTENTS", _
"x=" & w))
If vbOK = MsgBox("Would you like to switch " & _
"positions between the previous and the " & _
"current books for this edition?", _
vbQuestion + vbOKCancel + vbDefaultButton2, _
"This book already exits in volume " & x & _
" of this edition...") Then
switchPos = True
p = DLookup("BookPos", "VOLUMES_CONTENTS", "x=" _
& w)
b = Me!FirstBook.OldValue
DoCmd.RunSQL "DELETE FROM VOLUMES_CONTENTS " &_
"WHERE x=" & w
DoCmd.RunSQL "UPDATE VOLUMES_CONTENTS SET Book" _
& "=" & Me!FirstBook & " WHERE BookPos = 1 " _
& "AND Volume=" & v
Else
Cancel = True
MsgBox "Please choose another value for " & _
"FirstBook!", vbCritical, "This book is " & _
"already known as belonging to volume number " _
& x & " of this edition..."
Me!FirstBook.Undo
End If
End If
End If
End If
End If
Exit Sub
err_point: MsgBox Err.Source & " -> " & Err.Description, _
vbCritical, "Error in method Form_EDITIONS. " & _
"FirstBook_BeforeUpdate..."
Cancel = True
End Sub
'*****************************
Private Sub Form_AfterUpdate()
'*****************************
'enforces ontoness of Edition : VOLUMES -> EDITIONS
'(immediately after insertion of a new edition,
'inserts a corresponding first volume of it).
'Moreover, it also enforces constraint CVC4:
'for any edition, its first book should be
443
'the first one published in its first volume.
Dim w As Variant
On Error GoTo err_point
If switchPos Then
'enforces constraint CVC4
switchPos = False
DoCmd.RunSQL "INSERT INTO VOLUMES_CONTENTS (Volume, " _
& "Book, BookPos) VALUES (" & v & ", " & b & ", " _
& p & ")"
Else
'enforces ontoness of Edition : VOLUMES -> EDITIONS
w = DLookup("x", "VOLUMES", "Edition=" & Me!x)
If IsNull(w) Then
insertFirstVolume
End If
End If
On Error Resume Next
Forms!VOLUMES.Requery
Forms!VOLUMES!Edition.Requery
Forms!VOLUMES_CONTENTS.Requery
Forms!VOLUMES_CONTENTS!Volume.Requery
Exit Sub
err_point: MsgBox Err.Source & " -> " & Err.Description, _
vbCritical, "Error in method Form_EDITIONS." _
& "Form_AfterUpdate..."
End Sub
'******************************
Private Sub insertFirstVolume()
'******************************
'called by Form_AfterUpdate
Dim v As Long
On Error GoTo err_point
DoCmd.RunSQL "INSERT INTO VOLUMES (Edition) VALUES (" _
& Me!x & ")"
v = DLookup("x", "VOLUMES", "Edition=" & Me!x & _
" AND VNo=1")
DoCmd.RunSQL "INSERT INTO VOLUMES_CONTENTS (Volume, Book" _
& ", BookPos) VALUES (" & v & ", " & Me!FirstBook & ", 1)"
Exit Sub
err_point: MsgBox Err.Source & " -> " & Err.Description, _
vbCritical, "Error in method Form_EDITIONS." _
& "insertFirstVolume..."
End Sub
444
5.1.2 CP6
(bBORROWS)(FName(Subscriber(b)) NULLS
e-mail(Subscriber(b)) NULLS) (FName and e-mail should be compul-
sory for subscribers.)
Obviously, enforcing this constraint needs not VBA code, as it can be sim-
ply done by adding a corresponding filter to the SELECT statement of the
Subscriber combo-box from BORROWS (also see 4.1.2.1 above):
SELECT x, [e-mail] & " " & [FName] & " " & [LName]
AS [e-mail First and Last Names]
FROM PERSONS
WHERE [e-mail] Is Not Null AND FName Is Not Null
ORDER BY [e-mail] & " " & [FName] & " " & [LName];
5.1.3 CVC4
(eEDITIONS)(vcVOLUMES_CONTENTS)
(e = Edition(Volume(vc)) FirstBook(e) = Book(vc) BookPos(vc) = 1)
(For any edition, its first book should be the first one published in its first
volume.)
As we’ve already seen, this constraint is enforced by methods FirstBook_
BeforeUpdate and Form_AfterUpdate of class Form_EDITIONS (see
5.1.1 above).
5.1.4 CVC5
(eEDITIONS)(vc,vc’VOLUMES_CONTENTS)
(Edition(Volume(vc)) = Edition(Volume(vc’)) Book(vc) Book(vc’))
(No edition may contain same book more than once.)
As we’ve already seen, this constraint is enforced by method FirstBook_
BeforeUpdate of class Form_EDITIONS (see 5.1.1 above).
5.1.5 CBL5
(blBORROWS_LISTS)
(0 DueReturnDate(bl) – BorrowDate(Borrow(bl)) 300) (No
copy may be borrowed less than 0 days or more than 300 days.)
Obviously, this constraint is best enforceable by the DueReturnDate_Be-
foreUpdate method of class Form_ BORROWS_LISTS (Note that the cor-
responding form should never be used alone, but only as a sub-form of
445
BORROWS: otherwise, on blank lines Me!Borrow could not be always
defined!):
'********************************************************
Private Sub DueReturnDate_BeforeUpdate(Cancel As Integer)
'********************************************************
'enforces constraint CBL5: no copy may be borrowed less
'than 0 days or more than maxBorrowDays (300) days.
Dim bDate As Date
Dim days As Long
On Error GoTo err_point
If Me!DueReturnDate <> Me!DueReturnDate.OldValue Then
If Not IsNull(Me!DueReturnDate) Then
bDate = DLookup("BorrowDate", "BORROWS", "x = " & _
Me!Borrow)
days = DateDiff("y", bDate, Me!DueReturnDate)
If days < 0 Then
Cancel = True
MsgBox "Please specify a due return date greater " _
& "or equal to " & bDate & "!", vbCritical, _
"DueReturnDate value less than " _
& "the corresponding BorrowDate one..."
Me!DueReturnDate.Undo
ElseIf days > maxBorrowDays Then
Cancel = True
MsgBox "Please specify a due return date less or " _
& "equal to " & bDate + maxBorrowDays & "!", _
vbCritical, "DueReturnDate " & "value greater " _
& "than the corresponding BorrowDate one + " _
& maxBorrowDays & " days..."
Me!DueReturnDate.Undo
End If
End If
End If
Exit Sub
err_point: MsgBox Err.Source & " -> " & Err.Description, _
vbCritical, "Error in method Form_BORROWS_LISTS." _
& "DueReturnDate_BeforeUpdate..."
Cancel = True
End Sub
5.1.6 CBL6
(bl, bl’ BORROWS_LISTS) (Copy(bl) = Copy(bl’))
(ActualReturnDate(bl’) NULLS BorrowDate(Borrow(bl)
ActualReturnDate(bl’) ActualReturnDate(bl) NULLS
446
BorrowDate(Borrow(bl’) ActualReturnDate(bl)) (No copy may be
simultaneously borrowed to more than one subscriber.)
Obviously, this is a fine example of non-relational constraint whose logic
formula is much more complicated than the corresponding enforcement
code: in fact, each time that a copy is selected for lending, all we have to
do is to check whether or not there is another row in BORROWS_LISTS
for the same copy and having ActualReturnDate null (meaning that it has
not been yet returned).
Trivially, the best event for enforcing this constraint is the Copy_Before-
Update one of class Form_BORROWS_LISTS; unfortunately, although
this solution works fine, Access is displaying the following unexpected
and unpleasant (both for users and programmers) error message:
Figure A.1.18 Unexpected unpleasant Access error message
The workaround for it is to postpone enforcement up to the corresponding
Form_BeforeUpdate event:
'***********************************************
Private Sub Form_BeforeUpdate(Cancel As Integer)
'***********************************************
'enforces constraint CBL6: No copy may be simultaneously
'borrowed to more than one subscriber.
Dim v As Variant
On Error GoTo err_point
If Me.NewRecord Or Me!Copy <> Me!Copy.OldValue Then
v = DLookup("DueReturnDate", "BORROWS_LISTS", "Copy=" & _
Me!Copy & " AND ActualReturnDate Is Null AND x <>" _
& Me!x)
If Not IsNull(v) Then
Cancel = True
MsgBox "... so it cannot be lend: it is due to be " & _
447
"returned on " & v, vbCritical, _
"This copy is not yet returned..."
End If
End If
Exit Sub
err_point: MsgBox Err.Source & " -> " & Err.Description, _
vbCritical, "Error in method Form_BORROWS_LISTS." _
& "Form_BeforeUpdate..."
Cancel = True
End Sub
5.1.7 CBL7
(blBORROWS_LISTS)
(0 ActualReturnDate(bl) – BorrowDate(Borrow(bl)) 36,500)
(No copy may be returned before it was borrowed and after 100 years
since corresponding borrow date.)
Obviously, this constraint is best enforceable by the ActualReturnDate_
BeforeUpdate method of class Form_ BORROWS_LISTS:
'**********************************************************
Private Sub ActualReturnDate_BeforeUpdate(Cancel As
Integer)
'**********************************************************
'enforces constraint CBL7: no copy may be returned before
'it was borrowed or after 100 years since corresponding
'borrow date.
Dim bDate As Date
Dim days As Long
On Error GoTo err_point
If Me!ActualReturnDate <> Me!ActualReturnDate.OldValue Then
If Not IsNull(Me!ActualReturnDate) Then
bDate = DLookup("BorrowDate", "BORROWS", "x = " & _
Me!Borrow)
days = DateDiff("y", bDate, Me!ActualReturnDate)
If days < 0 Then
Cancel = True
MsgBox "Please specify a due return date greater " _
& "or equal to " & bDate & "!", vbCritical, _
"ActualReturnDate value less " _
& "than the corresponding BorrowDate one..."
Me!ActualReturnDate.Undo
ElseIf days > maxReturnDays Then
Cancel = True
MsgBox "Please specify a due return date less or " _
& "equal to " & bDate + maxReturnDays & "!", _
448
vbCritical, "ActualReturnDate value greater " _
& "than the corresponding " _
& "BorrowDate one + " & maxReturnDays & " days..."
Me!ActualReturnDate.Undo
End If
End If
End If
Exit Sub
err_point: MsgBox Err.Source & " -> " & Err.Description, _
vbCritical, "Error in method Form_BORROWS_LISTS." _
& "ActualReturnDate_BeforeUpdate..."
Cancel = True
End Sub
5.1.8 ACC1
(xCO-AUTHORS) (Coauthor(x) FirstAuthor Book(x)) (First au-
thors should appear only once in the coauthors lists, on the corresponding
first position.)
Obviously, this constraint is best enforceable by the Co_author_Before-
Update method of class Form_CO_AUTHORS:
'****************************************************
Private Sub Co_author_BeforeUpdate(Cancel As Integer)
'****************************************************
'enforces constraint ACC1: First authors should appear only
'once in the co-authors lists, on the corresponding first
'position.
Dim v As Variant
On Error GoTo err_point
If Me![Co-author] <> Me![Co-author].OldValue Then
If IsNull(Me![Co-author]) Then
Cancel = True
MsgBox "Choose a non-null value for CoAuthor! ", _
vbCritical, "For each book CoAuthors are " _
& "compulsory..."
Me![Co-author].Undo
Else
v = DLookup("x", "Books", "x=" & Me!Book & _
" AND FirstAuthor=" & Me![Co-author])
If Not IsNull(v) Then
Cancel = True
MsgBox "This CoAuthor is already stored in BOOKS " _
& "as the " & "FirstAuthor!", vbCritical, _
"For any book each CoAuthor should appear " _
& " only once in the CoAuthors list..."
449
Me![Co-author].Undo
End If
End If
End If
Exit Sub
err_point: MsgBox Err.Source & " -> " & Err.Description, _
vbCritical, "Error in method Form_CO_AUTHORS. " _
& "Co_author_BeforeUpdate..."
Cancel = True
End Sub
5.2 Oracle solutions
6. Database Usage
6.1 Access Queries and Reports
6.1.1 Queries
6.1.1.1 Overdue borrows
Compute the set of all copies that should have been returned at least k
(natural) days ago, in the descending order of the overdue period and then
ascending on borrowers e-mail addresses, last, and first names, and,
finally, copies inventory numbers.
Solution:
Obviously, data on overdue copies is stored in table BORROWS_LISTS:
any copy having a null in the ActualReturnDate is not yet returned and by
subtracting from the current date the DueReturnDate one and comparing
the result to k the requested set is easily computable.
Of course that from the Copy foreign key the corresponding InvNo may
be obtained through a join with table COPIES and that from the Borrow
foreign key the corresponding e-mail, FName, and LName may be ob-
tained through a join with tables BORROWS and then with table PER-
SONS (on foreign key Subscriber).
Consequently, the needed query is the following:
SELECT DateDiff("d",[DueReturnDate],Date()) AS OverdueDays,
450
[e-mail], FName, LName, InvNo
FROM PERSONS INNER JOIN (COPIES INNER JOIN (BORROWS
INNER JOIN BORROWS_LISTS
ON BORROWS.x = BORROWS_LISTS.Borrow)
ON COPIES.x = BORROWS_LISTS.Copy)
ON PERSONS.x = BORROWS.Subscriber
WHERE DateDiff("d",[DueReturnDate],Date())>=[k]
AND ActualReturnDate Is Null
ORDER BY DateDiff("d",[DueReturnDate],Date()) DESC,
[e-mail], LName, FName, InvNo;
Figure A.1.19 shows the result of running this query for k = 0:
Figure A.1.19 Result of running query OverdueDays for k = 0
6.1.1.2 Worse borrowers
Compute the set of borrowers that were late with at least k (natural) days
in returning, at least for n (natural) borrows, and at least m copies per bor-
row, in descending order of the sum of number of late returning copies
per borrow, then of the number of borrows, and then ascending on e-mail
addresses, last, and first names.
Solution:
Obviously, all copies that are not yet returned and are overdue already
with at least k days (that is exactly the set computed by the above 6.1.1.1
problem) are satisfying the conditions of this problem. Consequently, this
set may be computed by the following query:
451
SELECT DateDiff("d",[DueReturnDate],Date()) AS OverdueDays,
Borrow, Copy
FROM BORROWS_LISTS
WHERE DateDiff("d",[DueReturnDate],Date())>=[k]
AND ActualReturnDate Is Null;
Moreover, copies that were returned (that is their ActualReturnDate is not
null) may have also been returned with at least k days later than due; this
subset is computable by the following query:
SELECT DateDiff("d",[DueReturnDate],Date()) AS OverdueDays,
Borrow, Copy
FROM BORROWS_LISTS
WHERE ActualReturnDate Is Not Null AND
ActualReturnDate >= DueReturnDate AND
DateDiff("d", DueReturnDate, ActualReturnDate)>=[k];
The union of these two subsets (save it as OverdueCopies) is the base set
for computing the final result:
SELECT DateDiff("d",[DueReturnDate],Date()) AS OverdueDays,
Borrow, Copy
FROM BORROWS_LISTS
WHERE DateDiff("d",[DueReturnDate],Date())>=[k]
AND ActualReturnDate Is Null
UNION
SELECT DateDiff("d",[DueReturnDate],Date()) AS OverdueDays,
Borrow, Copy
FROM BORROWS_LISTS
WHERE ActualReturnDate Is Not Null AND
ActualReturnDate >= DueReturnDate AND
DateDiff("d", DueReturnDate, ActualReturnDate)>=[k];
Next step is computable directly from the above computed set, by
grouping on borrows (save it as OverdueBorrowsAndCopyNo): borrows
containing at least m overdue copies; the corresponding query is the
following (where OverdueCopies is the name of the above query):
OverdueBorrowsAndCopyNo:
SELECT Borrow, Count(Copy) AS OverdueCopiesNo
FROM OverdueCopies
GROUP BY Borrow
HAVING Count(Copy)>=[m];
452
For computing the number of borrows per subscriber and selecting only
those subscribers that have had at least n borrows, the needed query is:
SELECT Subscriber, Count(x) AS BorrowsNo
FROM BORROWS
GROUP BY Subscriber
HAVING Count(x)>=[n];
For further selecting from this set only those borrows that are computed
by OverdueBorrowsAndCopyNo, a join with it is necessary:
SELECT Subscriber, Count(x) AS BorrowsNo,
Sum(OverdueCopiesNo) AS SumOfOverdueCopiesNo
FROM BORROWS INNER JOIN OverdueBorrowsAndCopyNo
ON BORROWS.x = OverdueBorrowsAndCopyNo.Borrow
GROUP BY Subscriber
HAVING Count(x)>=[n];
The final result is obtainable from this one (save it as WorseBorrowers0)
by replacing subscriber with corresponding e-mail address, first, and last
name (through a join with PERSONS) and by ordering it in the requested
order:
WorseBorrowers:
SELECT SumOfOverdueCopiesNo, BorrowsNo, [e-mail],
FName, LName
FROM WorseBorrowers0 INNER JOIN PERSONS
ON WorseBorrowers0.Subscriber = PERSONS.x
ORDER BY SumOfOverdueCopiesNo DESC, BorrowsNo DESC,
[e-mail], LName, FName;
Figure A.1.20 shows the result of running this query for k = 0, n = m = 1:
Figure A.1.20 Result of running query WorseBorrowers for k = 0, n = m =
1
453
6.1.2 Reports
Design and develop a report with the following data: subscribers e-mail
addresses, first, and last names, borrow, due, and actual return dates,
number of overdue days, publisher, editions year and title, volumes num-
ber, ISBN, and title, copies inventory number, volumes first books title,
and their first authors first and last names;
Solution:
The corresponding subjacent query is the following:
SELECT PERSONS.[e-mail], PERSONS.FName, PERSONS.LName,
BorrowDate, DueReturnDate, ActualReturnDate,
IIf(IsNull([ActualReturnDate]),
IIf(Date()>[DueReturnDate],
DateDiff("d",[DueReturnDate],Date()),0),
IIf([ActualReturnDate]>[DueReturnDate],
DateDiff("d",[DueReturnDate],[ActualReturnDate]),
0)) AS OverdueDays, PUBLISHERS.PubName AS
Publisher, EDITIONS.EYear, EDITIONS.ETitle,
VOLUMES.VNo, VOLUMES.ISBN, VOLUMES.VTitle,
COPIES.InvNo, BOOKS.BTitle, AUTHORS.FName AS
AuthFName, AUTHORS.LName AS AuthLName
FROM (((EDITIONS INNER JOIN PUBLISHERS
ON EDITIONS.Publisher = PUBLISHERS.x)
INNER JOIN VOLUMES
ON EDITIONS.x = VOLUMES.Edition) INNER JOIN
(PERSONS INNER JOIN (COPIES INNER JOIN (BORROWS
INNER JOIN BORROWS_LISTS
ON BORROWS.x = BORROWS_LISTS.Borrow)
ON COPIES.x = BORROWS_LISTS.Copy)
ON PERSONS.x = BORROWS.Subscriber)
ON VOLUMES.x = COPIES.Volume) INNER JOIN
((BOOKS INNER JOIN PERSONS AS AUTHORS
ON BOOKS.FirstAuthor = AUTHORS.x) INNER JOIN
VOLUMES_CONTENTS ON BOOKS.x = VOLUMES_CONTENTS.Book)
ON VOLUMES.x = VOLUMES_CONTENTS.Volume
WHERE BookPos = 1;
The needed groupings are the following, in this order:
- On e-mail (with FName and LName too), ascendingly
- On BorrowDate, ascendingly
- On Publisher, ascendingly
- On EYear (with ETitle too), ascendingly
454
Sums of overdue days are computed for both editions, publishers, bor-
rows, subscribers, and overall.
Figure A.1.21 shows the corresponding report design view; figures A.1.22
and A.1.23 present the first and last part, respectively, of the results ob-
tained by running this report in preview mode.
Figure A.1.21 Borrows by subscribers report design view
455
Figure A.1.22 Borrows by subscribers report preview start screen
Figure A.1.23 Borrows by subscribers report preview end screen
456
6.2 Oracle Views and Stored Procedures
7. Conclusion I have chosen the subuniverse of a Library, for which I designed and im-
plemented a db meant to be the foundation of a db software application
for the management of books, subscribers, and borrowing.
First, I have applied the algorithm for assisting the business analysis of
this subuniverse, with which I have obtained a corresponding informal
description of it, a set of entity-relationship diagrams, and a list of asso-
ciated restrictions.
After applying the algorithm for translating entity-relationship diagrams
and restriction lists into mathematic schemes, I have refined the obtained
mathematical scheme by applying the following algorithms:
assistance of sets, functions, and constraints design
assistance of keys discovery
analysis of (structural) entity-relationship diagram cycles.
Next, I’ve applied the algorithm for translating mathematical schemes in-
to relational ones and non-relational constraint lists and, finally, the ones
for translating relational schemes into MS Access 2010 dbs and for en-
forcing non-relational constraints in MS Access 2010,
Finally, I have populated the obtained db with plausible demo data, I have
designed and developed two parameterized queries and a report, which
would very much help librarians to extract the most interesting data and
information from this db, and I fully documented the whole process, from
the business analysis and design stages, up the implementation and usage
ones.
457
8. Bibliography 1. Mancas, C. & Dicu, A. I. Databases Lab Notes, 2014, Bucharest
Polytechnic University.
2. Mancas, C. Databases Lecture Notes, 2014, Bucharest Polytech-
nic University.
3. Mancas, C. Conceptual Data Modeling and Database Design:
Analysis, Implementation and Optimization. A Fully Algorithmic
Approach, 2014, Apple Academic Press.
4. Microsoft Corp. Access 2010 Help.
5. IT Services. Microsoft Access 2010 An Essential Guide (Level 1),
2011, The University of Reading (freely downloadable from
http://www.reading.ac.uk/web/files/its/AccessEssen2010.pdf).
6. IT Services. Microsoft Access 2010 An Intermediate Guide (Level
2), 2011, The University of Reading (freely downloadable from
http://www.reading.ac.uk/web/files/its/AccessInter2010.pdf).
7. McDonald, M. Access 2010: The Missing Manual, 2010, O’Reilly
Media (freely downloadable from http://it-ebooks.info/book/104/).
458
Appendix 2: VBA and SQL functions by categories
A2.1 String processing functions
A2.1.1 LCase
LCase(string_expr) returns the corresponding string where all uppercase
characters have been converted to lowercase or Null if string_expr eva-
luates to Null. Lowercase letters and nonletter characters remain un-
changed.
A2.1.2 UCase
UCase(string_expr) returns the corresponding string where all lowercase
characters have been converted to uppercase or Null if string_expr eva-
luates to Null. Uppercase letters and nonletter characters remain un-
changed.
A2.1.3 Len
Len(string_expr) returns a Long containing the number of characters in a
string or the number of bytes required to store a variable or Null if string_
expr evaluates to Null. If string_expr is a Variant, Len treats it the same
as a String and returns the number of characters it contains. If string_expr
is a user-defined type, Len returns the size as it will be written to the file;
beware that Len may not be able to determine the actual number of sto-
rage bytes required when used with variable-length strings in user-defined
data types.
Use the LenB function with byte data contained in a string, as in double-
byte character set (DBCS) languages. Instead of returning the number of
characters in a string, LenB returns the number of bytes used to represent
that string. With user-defined types, LenB returns the in-memory size,
including any padding between elements.
A2.1.4 Left and Right
Left(string_expr, length) and Right(string_expr, length) return a Variant
(String) containing length characters from the left/right side of the string
to which string_expr evaluates or Null if string_expr evaluates to Null.
length should be a Variant (Long) numeric expression indicating how ma-
ny characters to return: if it is negative or 0, a zero-length string ("") is re-
turned; if it is greater than or equal to the number of characters in
string_expr, the entire corresponding string is returned.
459
Use the LeftB and RightB functions with byte data contained in a string.
Instead of specifying the number of characters to return, length specifies
for them the number of desired bytes.
A2.1.5 LTrim, RTrim, and Trim
LTrim(string_expr), RTrim(string_expr), and Trim(string_expr) return a
Variant (String) containing a copy of a specified string without leading
spaces (LTrim), trailing spaces (RTrim), or both leading and trailing spa-
ces (Trim) or Null if string_expr evaluates to Null.
A2.1.6 StrReverse
StrReverse(string_expr) returns a string in which the character order of
the specified string is reversed; if string_expr is a zero-length string (""),
a zero-length string is returned; if string_expr evaluates to Null, an error
occurs.
A2.1.7 InStr
InStr([start, ]string_expr, substring_expr[, compare]) returns a Variant
(Long) specifying the position of the first occurrence of substring_expr
within string_expr. start is an optional, but required if compare is speci-
fied, numeric expression that sets the starting position for each search; if
omitted, search begins at the first character position; if start contains Null,
an error occurs. compare is optional and specifies the type of string com-
parison; if it is Null, an error occurs; if it is omitted, the Option Compare
setting determines the comparison type; compare has to have one of the
following valid LocaleID (LCID) constants to use locale-specific rules in
the comparison:
Constant Value Description
vbUseCompareOption -1 Performs comparison using the setting of
the Option Compare statement.
vbBinaryCompare 0 Performs binary comparison.
vbTextCompare 1 Performs textual comparison.
vbDatabaseCompare 2 MS Access only: performs comparison
based on information in your db.
Table A2T.1 The LCID constants
460
InStr returns the following values:
When: InStr returns:
string_expr is zero-length 0
string_expr is Null Null
substring_expr is zero-length start
substring_expr is Null Null
substring_expr is not found 0
substring_expr is found within string_expr Position where match is
found
start > sub-string_expr 0
A2.1.8 InStrB
InStrB([start, ]string_expr, substring_expr[, compare]) is almost identical
to InStr, except for the returning value which is not the character, but the
corresponding byte position.
A2.1.9 InstrRev
InstrRev(string_expr, substring_expr[, start] [, compare]) is almost iden-
tical to InStr, except for the returning values and search direction (“Rev”
coming from “Reverse”), which from right (end of string_ expr) to left
(begin of string_expr). Its return values are:
When: InStrRev returns:
string_expr is zero-length 0
string_expr is Null Null
sub-string_expr is zero-length start
substring_expr is Null Null
substring_expr is not found 0
substring_expr is found within
string_expr
Position where match is
found
start > Len(substring_expr) 0
461
A2.1.10 Replace
Replace(expression, find, replace[, start[, count[, compare]]]) returns a
string obtained from expression in which the specified find substring has
been replaced with the replace substring for count times, beginning at po-
sition start, and concluding at the end of expression.
Argument Description
expression Required. String expression containing substring to replace.
Find Required. Substring being searched for.
Replace Required. Replacement substring.
Start Optional. Position within expression where substring search
is to begin. If omitted, 1 is assumed.
Count Optional. Number of substring substitutions to perform. If o-
mitted, the default value is -1, which means make all possible
substitutions.
compare Optional. Numeric value indicating the kind of comparison
to use when evaluating substrings. See table A2T.1 for its va-
lues.
Replace returns the following values:
When: Replace returns:
expression is zero-length Zero-length string ("")
expression is Null An error.
find is zero-length Copy of expression.
replace is zero-length Copy of expression with all occurences of find re-
moved.
start > Len(expression) Zero-length string.
count is 0 Copy of expression.
462
A2.1.11 StrComp
StrComp(string1, string2[, compare]) returns a Variant (Integer) indicat-
ing the result of a string comparison.
Argument Description
string1 Required. Any valid string expression.
string2 Required. Any valid string expression.
compare Optional. Specifies the type of string comparison. If the com-
pare argument is Null, an error occurs. If compare is omitted,
the Option Compare setting determines the type of compari-
son. See table A2T.1 for its values.
StrComp has the following return values:
When: StrComp returns:
string1 < string2 -1
string1 = string2 0
string1 > string2 1
string1 or string2 is Null Null
A2.1.12 Space
Space(number) returns a Variant (String) consisting of number of spaces.
Useful for formatting output and clearing data in fixed-length strings.
A2.1.13 String
String(number, character) returns a Variant (String) containing character
repeated number of times; if char(character) > 255, String converts cha-
racter to a valid ASCII character code using the formula character Mod
256.
463
Argument Description
Number Required; Long. Length of the returned string. If number eva-
luates to Null, Null is returned.
character Required; Variant. Character code specifying the character or
string expression whose first character is used to build the re-
turn string. If character evaluates to Null, Null is returned.
A2.1.14 StrConv
StrConv(string_expr, conversion, LCID) returns a Variant (String) con-
verted as specified.
Argument Description
string_expr Required. String expression to be converted.
conversion Required. Integer. The sum of values specifying the type of
conversion to perform.
LCID Optional. The LocaleID, if different than the system’s one.
(The system LocaleID is the default.)
Note that the valid constants for conversion (see following table) may be
combined (for example, vbUpperCase + vbWide), except when they are
mutually exclusive (for example, vbUnicode + vbFromUnicode). Con-
stants vbWide, vbNarrow, vbKatakana, and vbHiragana cause run-time
errors when used in locales where they do not apply.
The following are valid word separators for proper casing: Null (Chr$(0)),
horizontal tab (Chr$(9)), linefeed (Chr$(10)), vertical tab (Chr$(11)),
form feed (Chr$(12)), carriage return (Chr$(13)), space (SBCS)
(Chr$(32)). The actual value for a space varies by country/region for
DBCS.
When converting from a Byte array in ANSI format to a string, use the
StrConv function. When converting from such an array in Unicode for-
mat, use an assignment statement.
464
Valid conversion argument values are the following:
Constant Value Description
vbUpperCase 1 Converts the string to uppercase characters.
vbLowerCase 2 Converts the string to lowercase characters.
vbProperCase 3 Converts the first letter of every word in string
to uppercase.
vbWide* 4* Converts narrow (single-byte) characters in
string to wide (double-byte) characters.
vbNarrow* 8* Converts wide (double-byte) characters in
string to narrow (single-byte) characters.
vbKatakana** 16** Converts Hiragana characters in string to Kata-
kana characters.
vbHiragana** 32** Converts Katakana characters in string to Hira-
gana characters.
vbUnicode 64 Converts the string to Unicode using the default
code page of the system. (Not available on the
Macintosh.)
vbFromUnicode 128 Converts the string from Unicode to the default
code page of the system. (Not available on the
Macintosh.)
*Applies to Far East locales. **Applies to Japan only.
A2.1.15 Join
Join(sourcearray[, delimiter]) returns a string created by concatenating all
substrings contained in an array. It is the dual of Split.
Argument Description
sourcearray Required. One-dimensional array containing substrings to
be concatenated.
delimiter Optional. String character used to separate the substrings in
the returned string. If omitted, the space character (" ") is
used. If delimiter is a zero-length string (""), all items in the
list are concatenated with no delimiters.
465
A2.1.16 Split
Split(expression[, delimiter[, limit[, compare]]]) returns a zero-based,
one-dimensional array containing a specified number of substrings. It is
the dual of Join.
Argument Description
expression Required. String expression containing substrings and deli-
miters. If expression is a zero-length string(""), Split returns
an empty array, that is, an array with no elements and no da-
ta.
delimiter Optional. String character used to identify substring limits. If
omitted, the space character (" ") is assumed to be the delimi-
ter. If delimiter is a zero-length string, a single-element array
containing the entire expression string is returned.
limit Optional. Number of substrings to be returned; -1 indicates
that all substrings are returned.
compare Optional. Numeric value indicating the kind of comparison
to use when evaluating substrings. See table A2T.1 for its va-
lues.
A2.2 Data types associated functions
A2.2.1 IsNull
IsNull(expression) returns True if expression is Null; otherwise, returns
False; if expression consists of more than one variable, Null in any consti-
tuent variable causes True to be returned for the entire expression. The re-
quired expression argument is a Variant containing a numeric or string
expression.
Note that Null is not the same either as Empty, which indicates that a va-
riable has not yet been initialized, or as a zero-length string (""), which is
sometimes referred to as a null string.
Also note that expressions that you might expect to evaluate to True un-
der some circumstances, such as If Var = Null and If Var <>
Null, are always False, because any expression containing a Null is it-
self Null and, therefore, False.
466
A2.2.2 IsEmpty
IsEmpty(expression) returns True if expression is uninitialized, or is ex-
plicitly set to Empty; otherwise, it returns False; False is always returned
if expression contains more than one variable.The required expression ar-
gument is a Variant containing a numeric or string expression that should
evaluate to a single variable name.
A2.2.3 TypeName
TypeName(varname) returns a String that provides information about var-
name. The required varname argument is a Variant that may evaluate to
any variable name, except for variables of user-defined types.
Returned String Variable
object type An object whose type is objecttype
Byte Byte value
Integer Integer
Long Long integer
Single Single-precision floating-point number
Double Double-precision floating-point number
Currency Currency value
Decimal Decimal value
Date Date value
String String
Boolean Boolean value
Error Error value
Empty Uninitialized
Null No valid data
Object An object
Unknown An object whose type is unknown
Nothing Object variable that doesn't refer to an object
DataType() Array of DataType (including Variant) elements
467
A2.2.4 VarType
VarType(varname) returns an Integer indicating the subtype of varname.
The required varname argument is a Variant that may evaluate to any va-
riable name, except for variables of user-defined types. Returned values
are the following:
Constant Value Description
vbEmpty 0 Empty (uninitialized)
vbNull 1 Null (no valid data)
vbInteger 2 Integer
vbLong 3 Long integer
vbSingle 4 Single-precision floating-point number
vbDouble 5 Double-precision floating-point number
vbCurrency 6 Currency value
vbDate 7 Date value
vbString 8 String
vbObject 9 Object
vbError 10 Error value
vbBoolean 11 Boolean value
vbVariant 12 Variant (used only with arrays of
variants)
vbDataObject 13 A data access object
vbDecimal 14 Decimal value
vbByte 17 Byte value
vbLongLong 20 LongLong integer (Valid on 64-bit
platforms only)
vbUserDefinedType 36 Variants that contain user-defined types
vbArray 8192 Array
468
Note that vbArray is never returned by itself: it is always added to some
other value to indicate an array of a particular type; vbVariant is only re-
turned in conjunction with vbArray to indicate that varname is an array of
type Variant (for example, the value returned for an array of integers is
vbInteger + vbArray = 8194); if an object has a default property, the type
of the object's default property is returned.
A2.2.5 IsDate
IsDate(expression) returns True if expression evaluates to a valid date;
otherwise, it returns False. The required expression argument is a Variant
containing a date expression or string expression recognizable as a date or
time.59
A2.2.6 IsNumeric
IsNumeric(expression) returns True if expression is recognized as a num-
ber; otherwise, it returns False (including when expression is a date one).
The required expression argument is a Variant containing a numeric or
string expression.
A2.2.7 IsArray
IsArray(varname) returns True if varname evaluates to an array; other-
wise, it returns False.
A2.2.8 IsMissing
IsMissing(argname) returns a Boolean value indicating whether the optio-
nal Variant argument argname has been passed to a procedure: True if no
value has been passed for argname or, otherwise, False. The required
argname argument contains the name of an optional Variant procedure
argument.
If IsMissing is used on a ParamArray argument, it always returns False.
To detect an empty ParamArray, test to see if the array's upper bound is
less than its lower bound.
IsMissing does not work on simple data types (such as Integer or Double)
because, unlike Variants, they don't have a provision for a "missing" flag
bit. Because of this, the syntax for typed optional arguments allows you to
specify a default value. If the argument is omitted when the procedure is
called, then the argument will have this default value.
59
Note that in MS Windows, the range of valid dates is January 1, 100 A.D. through De-
cember 31, 9999 A.D.; this range vary among operating systems.
469
A2.2.9 IsObject
IsObject(identifier) returns a Boolean value indicating whether identifier
represents an object variable: True if identifier is a variable declared with
Object type or any valid class type, or if it is a Variant of VarType
vbObject, or a user-defined object; returns True even if the variable has
been set to Nothing; otherwise, it returns False. The required identifier ar-
gument is a variable name. Useful only in determining whether a Variant
is of VarType vbObject; this could occur if the Variant actually references
(or once referenced) an object, or if it contains Nothing. Always use error
trapping to be sure that an object reference is valid.
A2.2.10 IsError
IsError(expression) returns True if expression evaluates to an error;
otherwise, it returns False. The required expression argument can be any
valid expression.
Note that error values are created by converting real numbers to error
values using the CVErr function; IsError is used to determine if a nume-
ric expression represents an error.
A2.3 Date functions
A2.3.1 Date and Date$
Date() and Date$() return a Variant (Date) or a string, respectively, con-
taining the current system date. To set the system date, use the Date state-
ment; if you use Date and the calendar is Gregorian, Date$ behavior is
unchanged by the Calendar property setting.60
A2.3.2 Time and Time$
Time() and Time$() return a Variant (Date) or a string, respectively, con-
taining the current system time. (To set the system date, use the Time
statement.)
A2.3.3 Now
Now() returns a Variant (Date) containing the current system date and
time.
60
If the calendar is Hijri, Date$ returns a 10-character string of the form mm-dd-yyyy,
where mm (01-12), dd (01-30) and yyyy (1400-1523) are the Hijri month, day and year.
The equivalent Gregorian range is Jan 1, 1980 through Dec 31, 2099.
470
A2.3.4 DatePart
DatePart(interval, date[,firstdayofweek[, firstweekofyear]]) returns a Va-
riant (Integer) containing the specified part of date.
Argument Description
interval Required. String expression that is the interval of time
you want to return.
date Required. Variant (Date) value that you want to evalu-
ate.
firstdayofweek Optional. A constant that specifies the first day of the
week. If not specified, Sunday is assumed.
firstweekofyear Optional. A constant that specifies the first week of the
year. If not specified, the first week is assumed to be the
week in which January 1 occurs.
The interval argument has the following valid values:
Value Description
yyyy Year
q Quarter
m Month
y Day of year
d Day
w Weekday
ww Week
h Hour
n Minute
s Second
Table A2T.2 Date interval codes
471
The firstdayofweek argument has the following valid values:
Constant Value Description
vbUseSystem 0 Use the NLS API
setting.
vbSunday 1 Sunday (default)
vbMonday 2 Monday
vbTuesday 3 Tuesday
vbWednesday 4 Wednesday
vbThursday 5 Thursday
vbFriday 6 Friday
vbSaturday 7 Saturday
Table A2T.3 Weekday constants
The firstweekofyear argument has the following valid values:
Constant Value Description
vbUseSystem 0 Use the NLS API setting.
vbFirstJan1 1 Start with week in which January 1 occurs (de-
fault).
vbFirstFourDays 2 Start with the first week that has at least four
days in the new year.
vbFirstFullWeek 3 Start with first full week of the year.
Table A2T.4 First week of year constants
The firstdayofweek argument affects calculations that use the "w" and
"ww" interval symbols.
If date is a date literal, the specified year becomes a permanent part of
that date. However, if date is enclosed in double quotation marks (" "),
472
and you omit the year, the current year is inserted in your code each time
the date expression is evaluated. This makes it possible to write code that
can be used in different years.
If the Calendar property setting is Gregorian, date must be Gregorian.61
The returned date part is in the time period units of the current Arabic
calendar.62
A2.3.5 DateSerial
DateSerial(year, month, day) returns a Variant (Date) for the specified
year, month, and day.
Argument Description
year Required; Integer. Natural between 100 and 9999 or a nume-
ric expression that evaluates to such a natural.
month Required; Integer. Any numeric expression.
day Required; Integer. Any numeric expression.
To specify a date, such as December 31, 1991, the range of numbers for
each DateSerial argument should be in the accepted range for the unit;
that is, 1-31 for days and 1-12 for months.63
However, you can also speci-
fy relative dates for each argument using any numeric expression that re-
presents some number of days, months, or years before or after a certain
date.
When any argument exceeds its accepted range, it increments to the next
larger unit, as appropriate. For example, if you specify 35 days, it is eva-
luated as one month and some number of days, depending on where in the
year it is applied. If any argument is outside the range -32,768 to 32,767,
an error occurs. If the date specified by the three arguments falls outside
the acceptable range of dates, an error occurs too. 61
If the calendar is Hijri, the supplied date must be Hijri. 62
For example, if the current calendar is Hijri and the date part to be returned is the year,
the year value is a Hijri year. 63
Under Windows 98 or 2000, two digit years for the year argument are interpreted
based on user-defined machine settings. The default settings are: values between 0 and
29 are interpreted as the years 2000-2029; default values between 30 and 99 are interpre-
ted as the years 1930-1999; for all other year arguments, use a four-digit year (for exam-
ple, 1800). To be sure that the function returns the proper value, always use a four-digit
year.
473
For year, month, and day, if the Calendar property setting is Gregorian,
the supplied value is assumed to be Gregorian (also see footnote 60).The
returned date part is in the time period units of the current VBA calendar
(also see footnote 61). For the argument year, values between 0 and 99
are interpreted as the years 1400-1499. For all other year values, use the
complete four-digit year (for example, 1520).
A2.3.6 TimeSerial
TimeSerial(hour, minute, second) returns a Variant (Date) containing the
time for hour, minute, and second.
Argument Description
hour Required; Variant (Integer). Number between 0 (12:00 A.M.)
and 23 (11:00 P.M.), inclusive, or a numeric expression.
minute Required; Variant (Integer). Any numeric expression.
second Required; Variant (Integer). Any numeric expression.
To specify a time, such as 11:59:59, the range of numbers for each
TimeSerial argument should be in the normal range for the unit: that is, 0-
23 for hours and 0-59 for minutes and seconds. However, you can also
specify relative times for each argument using any numeric expression
that represents some number of hours, minutes, or seconds before or after
a certain time.64
When any argument exceeds its normal range, it increments to the next
larger unit, as appropriate. For example, if you specify 75 minutes, it is
evaluated as one hour and 15 minutes. If any argument is outside the
range -32,768 to 32,767, an error occurs. If the time specified by the three
arguments causes the date to fall outside the acceptable range of dates, an
error occurs.
A2.3.7 DateValue
The required date argument of DateValue(date) is normally a string ex-
pression representing a date from January 1, 100 through December 31,
9999. However, date can also be any expression that can represent a date,
a time, or both a date and time, in that range.
64
For example, TimeSerial(12 - 6, -15, 0) returns 15 minutes before (-15)
six hours before noon (12 - 6), which is 5:45:00 A.M.
474
If date includes only numbers separated by valid date separators, DateVa-
lue recognizes the order for month, day, and year according to the Short
Date format you specified for your system. DateValue also recognizes un-
ambiguous dates that contain month names, either in long or abbreviated
form. For example, in addition to recognizing 12/30/1991 and 12/30/ 91,
DateValue also recognizes December 30, 1991 and Dec 30, 1991.
If the year part of date is omitted, DateValue uses the current year from
your computer's system date. If the date argument includes time informa-
tion, DateValue doesn't return it. However, if date includes invalid time
information (such as "89:98"), an error occurs. If the Calendar property
setting is Gregorian, date must be Gregorian (also see footnote 60).65
A2.3.8 TimeValue
TimeValue(time) returns a Variant (Date) containing the corresponding
time; if time contains Null, Null is returned; if time contains date informa-
tion, TimeValue doesn't return it; if time includes invalid date information,
an error occurs. The required time argument should be a string expression
representing a time from 0:00:00 (12:00:00 A.M.) to 23:59:59 (11:59:59
P.M.); time can also be any expression that represents a time in that range.
You can enter valid times using a 12-hour or 24-hour clock. For example,
"2:24PM" and "14:24" are both valid time values.
A2.3.9 DateAdd
DateAdd(interval, number, date) returns a Variant (Date) containing a
date to which a specified time interval has been added.
Argument Description
interval Required. String expression that is the interval of time you
want to add. See table A2T.2 for its valid values.
number Required. Numeric expression that is the number of intervals
you want to add. It can be positive (to get dates in the future)
or negative (to get dates in the past).
Date Required. Variant (Date) or literal representing date to which
the interval is added.
65
If the supplied date is Hijri, date is a String representing a date from 1/1/100
(Gregorian Aug 2, 718) through 4/3/9666 (Gregorian Dec 31, 9999).
475
To add days to date, you can use Day of Year ("y"), Day ("d"), or Week-
day ("w"). When you use the "w" interval (which includes all the days of
the week, Sunday through Saturday) to add days to a date, the DateAdd
function adds the total number of days that you specified to the date,
instead of adding just the number of workdays (Monday through Friday)
to the date, as you might expect.
The DateAdd function won't return an invalid date. The following exam-
ple adds one month to January 31: DateAdd("m", 1, "31-Jan-
95"). In this case, DateAdd returns 28-Feb-95, not 31-Feb-95. If date is
31-Jan-96, it returns 29-Feb-96, because 1996 is a leap year.
If the calculated date would precede the year 100 (that is, you subtract
more years than are in date), an error occurs. If number isn't a Long value,
it is rounded to the nearest whole number before being evaluated.
The format of the return value for DateAdd is determined by Control Pa-
nel settings, not by the format that is passed in date argument. If the Ca-
lendar property setting is Gregorian, the supplied date must be Gregorian
(also see footnote 60). If month values are names, the name must be con-
sistent with the current Calendar property setting. To minimize the possi-
bility of month names conflicting with the current Calendar property set-
ting, enter numeric month values (Short Date format).
A2.3.10 DateDiff
DateDiff(interval, date1, date2[, firstdayofweek[, firstweekofyear]]) re-
turns a Variant (Long) specifying the number of time intervals between
date1 and date2.
Argument Description
interval Required. String expression that is the interval of time
you use to calculate the difference between date1 and
date2. See table A2T.1 for its valid values.
date1, date2 Required; Variant (Date). Two dates you want to use in
the calculation.
firstdayofweek Optional. A constant that specifies the first day of the
week. If not specified, Sunday is assumed. See table
A2T.3 for its valid values.
476
firstweekofyear Optional. A constant that specifies the first week of the
year. If not specified, the first week is assumed to be the
week in which January 1 occurs. See table A2T.4 for its
valid values.
To calculate the number of days between date1 and date2, use either Day
of year ("y") or Day ("d"). When interval is Weekday ("w"), DateDiff re-
turns the number of weeks between the two dates. If date1 falls on a Mon-
day, DateDiff counts the number of Mondays until date2, counting date2
but not date1. If interval is Week ("ww"), however, DateDiff returns the
number of calendar weeks between the two dates, counting the number of
Sundays between date1 and date2; it counts date2 if it falls on a Sunday,
but it doesn't count date1, even if it does fall on a Sunday.
If date1 refers to a later point in time than date2, DateDiff returns the cor-
responding negative number.
The firstdayofweek argument affects calculations that use the "w" and
"ww" interval symbols.
If date1 or date2 is a date literal, the specified year becomes a permanent
part of that date. However, if date1 or date2 is enclosed in double quota-
tion marks (" ") and you omit the year, the current year is inserted in your
code each time the date1 or date2 expressions are evaluated. This makes
it possible to write code that can be used in different years.
When comparing December 31 to January 1 of the immediately succeed-
ing year, DateDiff for Year ("yyyy") returns 1 even though only a day has
elapsed.
If the Calendar property setting is Gregorian, date1 and date2 must be
Gregorian (also see footnote 60).
A2.3.11 Day
Day(date) returns a Variant (Integer) specifying the natural between 1
and 31 that corresponds to the day of the month of date. The required date
argument is any Variant, numeric expression, string expression, or any
combination, that can be evaluated to a date. If date contains Null, Null is
returned. If the Calendar property setting is Gregorian, the returned inte-
ger represents the corresponding Gregorian day (also see footnote 60).
477
A2.3.12 Weekday
Weekday(date, [firstdayofweek]) returns a Variant (Integer) containing a
natural corresponding to the day of the week of date (for actual returned
values see constants vbSunday to vbSaturday in table A2T.3).
Argument Description
Date Required. Variant, numeric expression, string expression,
or any combination that can be evaluated to a date. If
date contains Null, Null is returned.
firstdayofweek Optional. A constant that specifies the first day of the
week. If not specified, vbSunday is assumed. See table
A2T.3 for its valid values.
If the Calendar property setting is Gregorian, the returned integer repre-
sents the corresponding Gregorian day (also see footnotes 60 and 64).
A2.3.13 WeekdayName
WeekdayName(weekday, abbreviate, firstdayofweek) returns a string indi-
cating the specified day of the week.
Argument Description
weekday Required. The numeric designation for the day of the
week. Numeric value of each day depends on setting of
the firstdayofweek setting.
abbreviate Optional. Boolean value that indicates if the weekday
name is to be abbreviated. If omitted, the default is False,
which means that the weekday name is not abbreviated.
firstdayofweek Optional. Numeric value indicating the first day of the
week. See table A2T.3 for its valid values.
A2.3.14 Month
Month(date) returns a Variant (Integer) specifying a natural between 1
and 12, representing the month of the year of date. The required date ar-
gument is any Variant, numeric expression, string expression, or any
combination that can be evaluated to a date. If date contains Null, Null is
returned. If the Calendar property setting is Gregorian, the returned inte-
478
ger represents the corresponding Gregorian day of the week (also see
footnotes 60 and 64).
A2.3.15 MonthName
MonthName(month[, abbreviate]) returns a string indicating the specified
month.
Argument Description
month Required. The numeric designation of the month. For exam-
ple, January is 1, February is 2, and so on.
abbreviate Optional. Boolean value that indicates if the month name is
to be abbreviated. If omitted, the default is False, which
means that the month name is not abbreviated.
A2.3.16 Year
Year(date) returns a Variant (Integer) containing a natural representing
the year of date. The required date argument is any Variant, numeric ex-
pression, string expression, or any combination that can be evaluated to a
date. If date contains Null, Null is returned. If the Calendar property set-
ting is Gregorian, the returned integer represents the corresponding Gre-
gorian year (also see footnotes 60 and 64).
A2.3.17 Hour
Hour(time) returns a Variant (Integer) specifying a natural between 0 and
23 representing the hour of time. The required time argument is any Vari-
ant, numeric expression, string expression, or any combination that can be
evaluated to a time value. If time contains Null, Null is returned.
A2.3.18 Minute
Minute(time) returns a Variant (Integer) specifying a natural between 0
and 59 representing the minute of time. The required time argument is any
Variant, numeric expression, string expression, or any combination that
can be evaluated to a time value. If time contains Null, Null is returned.
A2.3.19 Second
Second(time) returns a Variant (Integer) specifying a natural between 0
and 59 representing the second of time. The required time argument is any
Variant, numeric expression, string expression, or any combination that
can be evaluated to a time value. If time contains Null, Null is returned.
479
A2.4 Numeric functions
A2.4.1 Abs
Abs(number) returns a value of the same type as number specifying the
absolute value66
of number. The required number argument can be any
valid numeric expression. If number contains Null, Null is returned; if it is
an uninitialized variable, zero is returned.
A2.4.2 Int and Fix
Int(number) and Fix(number) return the integer portion of number (re-
move its fractional part). The required number argument is a Double or
any valid numeric expression. If number contains Null, Null is returned.
The difference between Int and Fix is that if number is negative, Int re-
turns the first negative integer less than or equal to number, whereas Fix
returns the first negative integer greater than or equal to number.67
Fix(number) = Sgn(number) * Int(Abs(number)).
A2.4.3 Partition
Partition(number, start, stop, interval) returns a Variant (String) indicat-
ing where number occurs within a calculated series of ranges. If any of
the arguments is Null, Partition returns a Null.
Argument Description
number Required. Integer to be evaluated against the ranges.
start Required. Natural that is the start of the overall range of
numbers.
stop Required. Natural that is the end of the overall range of
numbers. stop > start.
The following table shows how the ranges are determined using three sets
of start, stop, and interval values. The First Range and Last Range co-
lumns show what Partition returns. Ranges are represented by lowerva-
lue:uppervalue, where the low end (lowervalue) of the range is separated
from the high end (uppervalue) of the range with a colon (:).
66
The absolute value of a number is its unsigned magnitude; for example, both ABS(-1)
and ABS(1) return 1. 67
For example, Int converts -8.4 to -9, whereas Fix converts it to -8.
480
start stop interval Before
First
First
Range
Last Range After
Last
0 99 5 " :-1" " 0: 4" " 95: 99" " 100: "
20 199 10 " : 19" " 20: 29" " 190: 199" " 200: "
100 1010 20 " : 99" " 100:
119"
" 1000:
1010"
" 1011: "
In the above table, the third line shows the result when start and stop de-
fine a set of numbers that can't be evenly divided by interval. The last
range extends to stop (11 numbers) even though interval is 20.
If necessary, Partition returns a range with enough leading spaces so that
there are the same number of characters to the left and right of the colon
as there are characters in stop, plus one. This ensures that if you use Par-
tition with other numbers, the resulting text will be handled properly dur-
ing any subsequent sort operation.
If interval is 1, the range is number:number, regardless of the start and
stop values. For example, if interval is 1, number is 100 and stop is 1000,
Partition returns " 100: 100".
A2.4.4 Round
Round(expression [,numdecimalplaces]) returns a number rounded to a
specified number of decimal places.
Argument Description
expression Required. Numeric expression being rounded.
numdecimalplaces Optional. Number indicating how many places to the
right of the decimal are included in the rounding. If
omitted, integers are returned.
A2.4.5 Sgn
Sgn(number) returns a Variant (Integer) indicating the sign of number.
The required number argument can be any valid numeric expression.
481
When number is Sgn returns
Greater than
zero
1
Equal to zero 0
Less than zero -1
A2.4.6 Rnd
Rnd[(number)] returns a Single containing a random number less than 1
and greater than or equal to zero. The optional number argument is a Sin-
gle or any valid numeric expression. The value of number determines
how random numbers are generated: for any given initial seed, the same
number sequence is generated, because each successive call to Rnd uses
the previous number as a seed for the next number in the sequence.
When number is Rnd generates
Less than zero The same number every time, using number as the
seed.
Greater than
zero
The next random number in the sequence.
Equal to zero The most recently generated number.
Not supplied The next random number in the sequence.
Before calling Rnd, use the Randomize statement without argument value
to initialize the random-number generator with a seed based on the system
timer. To produce random integers in a given range, use the formula
Int((upperbound - lowerbound + 1) * Rnd + lowerbound),
where upperbound is the highest number in the range, and lowerbound is
the lowest one.
To repeat sequences of random numbers, call Rnd with a negative argu-
ment value immediately before using Randomize with a numeric argu-
ment. Using Randomize with the same value for number does not repeat
the previous sequence.
482
A2.4.7 Trigonometric and other algebraic functions
Function Description
Sqr Square root
Exp Exponential
Log Logarithm (natural)
Atn Arctangent
Cos Cosine
Sin Sine
Tan Tangent
Note that, for all of them too, when their arguments contain Null, Null is
returned.
A2.4.8 Derived Math Functions
The following is a list of nonintrinsic math functions that can be derived
from the intrinsic ones:
Function Derived equivalents
Secant Sec(X) = 1 / Cos(X)
Cosecant Cosec(X) = 1 / Sin(X)
Cotangent Cotan(X) = 1 / Tan(X)
Inverse
Sine
Arcsin(X) = Atn(X / Sqr(-X * X + 1))
Inverse
Cosine
Arccos(X) = Atn(-X / Sqr(-X * X + 1)) + 2 * Atn(1)
Inverse
Secant
Arcsec(X) = Atn(X / Sqr(X * X - 1)) + Sgn((X) - 1) * (2 *
Atn(1))
Inverse
Cosecant
Arccosec(X) = Atn(X / Sqr(X * X - 1)) + (Sgn(X) - 1) * (2 *
Atn(1))
Inverse
Cotangent
Arccotan(X) = Atn(X) + 2 * Atn(1)
483
Hyperbolic
Sine
HSin(X) = (Exp(X) - Exp(-X)) / 2
Hyperbolic
Cosine
HCos(X) = (Exp(X) + Exp(-X)) / 2
Hyperbolic
Tangent
HTan(X) = (Exp(X) - Exp(-X)) / (Exp(X) + Exp(-X))
Hyperbolic
Secant
HSec(X) = 2 / (Exp(X) + Exp(-X))
Hyperbolic
Cosecant
HCosec(X) = 2 / (Exp(X) - Exp(-X))
Hyperbolic
Cotangent
HCotan(X) = (Exp(X) + Exp(-X)) / (Exp(X) - Exp(-X))
Inverse
Hyperbolic
Sine
HArcsin(X) = Log(X + Sqr(X * X + 1))
Inverse
Hyperbolic
Cosine
HArccos(X) = Log(X + Sqr(X * X - 1))
Inverse
Hyperbolic
Tangent
HArctan(X) = Log((1 + X) / (1 - X)) / 2
Inverse
Hyperbolic
Secant
HArcsec(X) = Log((Sqr(-X * X + 1) + 1) / X)
Inverse
Hyperbolic
Cosecant
HArccosec(X) = Log((Sgn(X) * Sqr(X * X + 1) + 1) / X)
Inverse
Hyperbolic
Cotangent
HArccotan(X) = Log((X + 1) / (X - 1)) / 2
Logarithm
to base N
LogN(X) = Log(X) / Log(N)
484
A2.5 Conversion functions Each such function coerces an expression to the corresponding data type,
takes a required string or numeric expression argument, and returns the
corresponding converted value, whenever corresponding conversion is
possible. If the expression passed to the function is outside the range of
the data type being converted to, an error occurs.
Note that conversion functions must be used to explicitly assign
LongLong (including LongPtr on 64-bit platforms) to smaller integral
types. Implicit conversions of LongLong to smaller integrals are not
allowed.
In general, you can document your code using the data-type conversion
functions to show that the result of some operation should be expressed as
a particular data type rather than the default data type. For example, use
CCur to force currency arithmetic in cases where single-precision,
double-precision, or integer arithmetic normally would occur.
Function Return
Type
Range for expression argument
CBool Boolean Any valid string or numeric expression.
CByte Byte 0 to 255
CCur Currency -922,337,203,685,477.5808 to
922,337,203,685,477.5807
CDate Date Any valid date expression.
CDbl Double -1.79769313486231E308 to -
4.94065645841247E-324 for negative values;
4.94065645841247E-324 to
1.79769313486232E308 for positive values
CDec Decimal +/-79,228,162,514,264,337,593,543,950,335 for
zero-scaled numbers, that is numbers with no
decimal places. For numbers with 28 decimal
places, the range is +/-
7.9228162514264337593543950335. The
smallest possible non-zero number is
0.0000000000000000000000000001.
485
CInt Integer -32,768 to 32,767; fractions are rounded.
CLng Long -2,147,483,648 to 2,147,483,647; fractions are
rounded.
CLngLng LongLong -9,223,372,036,854,775,808 to
9,223,372,036,854,775,807; fractions are
rounded. (Valid on 64-bit platforms only.)
CLngPtr LongPtr -2,147,483,648 to 2,147,483,647 on 32-bit
systems, -9,223,372,036,854,775,808 to
9,223,372,036,854,775,807 on 64-bit systems;
fractions are rounded for 32-bit and 64-bit
systems.
CSng Single -3.402823E38 to -1.401298E-45 for negative
values; 1.401298E-45 to 3.402823E38 for
positive values.
CStr String Returns for CStr depend on the expression
argument.
CVar Variant Same range as Double for numerics. Same range
as String for non-numerics.
You should use the data-type conversion functions instead of Val to
provide internationally aware conversions from one data type to another.
For example, when you use CCur, different decimal separators, different
thousand separators, and various currency options are properly recognized
depending on the locale setting of your computer.
When the fractional part is exactly 0.5, CInt and CLng always round it to
the nearest even number.68
CInt and CLng differ from the Fix and Int
functions, which truncate, rather than round, the fractional part of a
number. Also, Fix and Int always return a value of the same type as is
passed in.
Use the IsDate function to determine if date can be converted to a date or
time. CDate recognizes date literals and time literals as well as some
numbers that fall within the range of acceptable dates. When converting a
number to a date, the whole number portion is converted to a date. Any
68
For example, 0.5 rounds to 0 and 1.5 rounds to 2.
486
fractional part of the number is converted to a time of day, starting at
midnight.
CDate recognizes date formats according to the locale setting of your sys-
tem. The correct order of day, month, and year may not be determined if
it is provided in a format other than one of the recognized date settings. In
addition, a long date format is not recognized if it also contains the day-
of-the-week string.69
CDec does not return a discrete data type; instead, it always returns a
Variant whose value has been converted to a Decimal subtype.
A2.5.1 Str
Str(number) returns a Variant (String) representation of number. The
required number argument is a Long containing any valid numeric
expression. Str recognizes only the period (.) as a valid decimal separator.
When different decimal separators may be used (for example, in interna-
tional applications), use CStr to convert a number to a string.
When numbers are converted to strings, a leading space is always re-
served for the sign of number. If number is positive, the returned string
contains a leading space and the plus sign is implied.
Use the Format function to convert numeric values you want formatted as
dates, times, or currency or in other user-defined formats. Unlike Str, the
Format function doesn't include a leading space for the sign of number.
A2.5.2 Val
Val(string) returns the numbers contained in string as a numeric value of
appropriate type. The required string argument is any valid string expres-
sion. Val stops reading the string at the first character it can't recognize as
part of a number.70
Symbols and characters that are often considered parts
of numeric values, such as dollar signs and commas, are not recognized.
However, the function recognizes the radix prefixes &O (for octal) and
69
A CVDate function is also provided for compatibility with previous versions of VB.
The syntax of the CVDate function is identical to that of CDate; however, CVDate
returns a Variant whose subtype is Date instead of an actual Date type. Since there is
now an intrinsic Date type, there is no further need for CVDate. The same effect can be
achieved by converting an expression to a Date, and then assigning it to a Variant. This
technique is consistent with the conversion of all other intrinsic types to their equivalent
Variant subtypes. 70
For example, Val(" 1615 198th Street N.E.") = 1615198
487
&H (for hexadecimal)71
. Blanks, tabs, and linefeed characters are stripped
from the argument. Val recognizes only the period (.) as a valid decimal
separator; when different decimal separators are used, as in international
applications, use CDbl instead to convert a string to a number.
A2.5.3 Format
Format(expression[,format [,firstdayofweek [,firstweekofyear]]]) returns a
Variant (String) containing expression formatted according to the instruc-
tions contained in format.
Argument Description
expression Required. Any valid expression.
Format Optional. A valid named or user-defined format
expression.
firstdayofweek Optional. A constant that specifies the first day of the
week. See table A2T.3 for its valid values.
firstweekofyear Optional. A constant that specifies the first week of the
year. See table A2T.4 for its valid values.
To Format Do This
Numbers Use predefined named numeric formats or create
user-defined numeric formats.
Dates and times Use predefined named date/time formats or create
user-defined date/time formats.
Date and time serial
numbers
Use date and time formats or numeric formats.
Strings Create your own user-defined string formats.
If you try to format a number without specifying format, Format provides
functionality similar to the Str function, although it is internationally
aware. However, positive numbers formatted as strings using Format
don't include a leading space reserved for the sign of the value, while
those converted using Str retain the leading space.
71
For example, Val("&HFFFF") = -1
488
If you are formatting a non-localized numeric string, you should use a
user-defined numeric format to ensure that you get the look you want.
If the Calendar property setting is Gregorian and format specifies date
formatting, the supplied expression must be Gregorian (also see footnote
60). If the calendar is Gregorian, the meaning of format expression sym-
bols is unchanged.72
A2.5.3.1 Different Formats for Different Numeric Values
A user-defined format expression for numbers can have from one to four
sections separated by semicolons.73
If the format argument contains one
of the named numeric formats, only one section is allowed.
If you use: the result is:
One section
only
The format expression applies to all values.
Two
sections
The first section applies to positive values and zeros, the se-
cond to negative values.
Three
sections
The first section applies to positive values, the second to
negative values, and the third to zeros.
Four
sections
The first section applies to positive values, the second to
negative values, the third to zeros, and the fourth to Null
values.
If you include semicolons with nothing between them, the missing section
is printed using the format of the positive value.74
A2.5.3.2 Different Formats for Different String Values
A format expression for strings can have one section or two sections
separated by a semicolon (;).
72
If the calendar is Hijri, all date format symbols (for example, dddd, mmmm, yyyy) have
the same meaning but apply to the Hijri calendar. Format symbols remain in English;
symbols that result in text display (for example, AM and PM) display the string (English
or Arabic) associated with that symbol. The range of certain symbols changes when the
calendar is Hijri. 73
For example, "$#,##0;($#,##0)" has two sections: the first defines the format
for positive values and zeros; the second one defines the format for negative values. 74
For example, "$#,##0;;\Z\e\r\o" displays positive and negative values using
the format in the first section and displays "Zero" if the value is zero.
489
If you use: the result is:
One section
only
The format applies to all string data.
Two
sections
The first section applies to string data, whereas the second
to Null values and zero-length strings ("").
A2.5.3.3 Named Date/Time Formats
The following table identifies the predefined date and time format names:
Format
Name
Description
General
Date
Display a date and/or time. For real numbers, display a date
and time, for example, 4/3/93 05:34 PM. If there is no fractio-
nal part, display only a date, for example, 4/3/93. If there is no
integer part, display time only, for example, 05:34 PM. Date
display is determined by your system settings.
Long
Date
Display a date according to your system's long date format.
Medium
Date
Display a date using the medium date format appropriate for
the language version of the host application.
Short
Date
Display a date using your system's short date format.
Long
Time
Display a time using your system's long time format; includes
hours, minutes, seconds.
Medium
Time
Display time in 12-hour format using hours and minutes and
the AM/PM designator.
Short
Time
Display a time using the 24-hour format, for example, 17:45.
490
A2.5.3.4 Named Numeric Formats
The following table identifies the predefined numeric format names:
Format
name
Description
General
Number
Display number with no thousand separators.
Currency Display number with thousand separators, if appropriate;
display two digits to the right of the decimal separator.
Output is based on system locale settings.
Fixed Display at least one digit to the left and two digits to the
right of the decimal separator.
Standard Display number with thousand separators, at least one digit
to the left and two digits to the right of the decimal separa-
tor.
Percent Display number multiplied by 100 with a percent sign (%)
appended to the right; always display two digits to the right
of the decimal separator.
Scientific Use standard scientific notation.
Yes/No Display No if number is 0; otherwise, display Yes.
True/False Display False if number is 0; otherwise, display True.
On/Off Display Off if number is 0; otherwise, display On.
491
A2.5.3.5 User-Defined String Formats
You can use any of the following characters to create a format expression
for strings:
Character Description
@ Character placeholder. Display a character or a space. If the
string has a character in the position where the at symbol (@)
appears in the format string, display it; otherwise, display a
space in that position. Placeholders are filled from right to left
unless there is an exclamation point character (!) in the for-
mat string.
& Character placeholder. Display a character or nothing. If the
string has a character in the position where the ampersand
(&) appears, display it; otherwise, display nothing. Placehol-
ders are filled from right to left unless there is an exclamation
point character (!) in the format string.
< Force lowercase. Display all characters in lowercase format.
> Force uppercase. Display all characters in uppercase format.
! Force left to right fill of placeholders. The default is to fill
placeholders from right to left.
492
A2.5.3.6 User-Defined Date/Time Formats
The following table identifies characters you can use to create user-de-
fined date/time formats:
Character Description
(:) Time separator. In some locales, other characters may be used
to represent the time separator. The time separator separates
hours, minutes, and seconds when time values are formatted.
The actual character used as the time separator in formatted
output is determined by your system settings.
(/) Date separator. In some locales, other characters may be used
to represent the date separator. The date separator separates
the day, month, and year when date values are formatted. The
actual character used as the date separator in formatted output
is determined by your system settings.
c Display the date as ddddd and display the time as ttttt, in that
order. Display only date information if there is no fractional
part to the date serial number; display only time information
if there is no integer portion.
d Display the day as a number without a leading zero (1 - 31).
dd Display the day as a number with a leading zero (01 - 31).
ddd Display the day as an abbreviation (Sun - Sat).
dddd Display the day as a full name (Sunday - Saturday).
ddddd Display the date as a complete date (including day, month,
and year), formatted according to your system's short date
format setting. The default short date format is m/d/yy.
dddddd Display a date serial number as a complete date (including
day, month, and year) formatted according to the long date
setting recognized by your system. The default long date for-
mat is mmmm dd, yyyy.
aaaa The same as dddd, only it's the localized version of the string.
w Display the day of the week as a number (1 for Sunday
through 7 for Saturday).
493
ww Display the week of the year as a number (1 - 54).
m Display the month as a number without a leading zero (1 -
12). If m immediately follows h or hh, the minute rather than
the month is displayed.
mm Display the month as a number with a leading zero (01 - 12).
If m immediately follows h or hh, the minute rather than the
month is displayed.
mmm Display the month as an abbreviation (Jan - Dec).
mmmm Display the month as a full month name (January – Decem-
ber).
oooo The same as mmmm, only it's the localized version of the
string.
q Display the quarter of the year as a number (1 - 4).
y Display the day of the year as a number (1 - 366).
yy Display the year as a 2-digit number (00 - 99).
yyyy Display the year as a 4-digit number (100 - 9999).
h Display the hour as a number without leading zeros (0 - 23).
Hh Display the hour as a number with leading zeros (00 - 23).
N Display the minute as a number without leading zeros (0 -
59).
Nn Display the minute as a number with leading zeros (00 - 59).
S Display the second as a number without leading zeros (0 -
59).
Ss Display the second as a number with leading zeros (00 - 59).
t t t t t Display a time as a complete time (including hour, minute,
and second), formatted using the time separator defined by
the time format recognized by your system. A leading zero is
displayed if the leading zero option is selected and the time is
before 10:00 A.M. or P.M. The default time format is
h:mm:ss.
494
AM/PM Use the 12-hour clock and display an uppercase AM with any
hour before noon; display an uppercase PM with any hour be-
tween noon and 11:59 P.M.
am/pm Use the 12-hour clock and display a lowercase AM with any
hour before noon; display a lowercase PM with any hour be-
tween noon and 11:59 P.M.
A/P Use the 12-hour clock and display an uppercase A with any
hour before noon; display an uppercase P with any hour be-
tween noon and 11:59 P.M.
a/p Use the 12-hour clock and display a lowercase A with any
hour before noon; display a lowercase P with any hour betwe-
en noon and 11:59 P.M.
AMPM Use the 12-hour clock and display the AM string literal as de-
fined by your system with any hour before noon; display the
PM string literal as defined by your system with any hour be-
tween noon and 11:59 P.M. AMPM can be either uppercase
or lowercase, but the case of the string displayed matches the
string as defined by your system settings. The default format
is AM/PM.
495
A2.5.3.7 User-Defined Numeric Formats
The following table identifies characters you can use to create user-de-
fined number formats:
Character Description
None Display the number with no formatting.
(0) Digit placeholder. Display a digit or a zero. If the expression
has a digit in the position where the 0 appears in the format
string, display it; otherwise, display a zero in that position.
If the number has fewer digits than there are zeros (on either
side of the decimal) in the format expression, display leading
or trailing zeros. If the number has more digits to the right of
the decimal separator than there are zeros to the right of the
decimal separator in the format expression, round the number
to as many decimal places as there are zeros. If the number
has more digits to the left of the decimal separator than there
are zeros to the left of the decimal separator in the format ex-
pression, display the extra digits without modification.
(#) Digit placeholder. Display a digit or nothing. If the expres-
sion has a digit in the position where # appears in the format
string, display it; otherwise, display nothing in that position.
This symbol works like the 0 digit placeholder, except that
leading and trailing zeros aren't displayed if the number has
the same or fewer digits than there are # characters on either
side of the decimal separator in the format expression.
(.) Decimal placeholder. In some locales, a comma is used as
the decimal separator. The decimal placeholder determines
how many digits are displayed to the left and right of the de-
cimal separator. If the format expression contains only num-
ber signs to the left of this symbol, numbers smaller than 1
begin with a decimal separator. To display a leading zero dis-
played with fractional numbers, use 0 as the first digit place-
holder to the left of the decimal separator. The actual charac-
ter used as a decimal placeholder in the formatted output de-
pends on the Number Format recognized by your system.
(%) Percentage placeholder. The expression is multiplied by 100.
The percent character (%) is inserted in the position where it
496
appears in the format string.
(,) Thousand separator. In some locales, a period is used as a
thousand separator. The thousand separator separates thou-
sands from hundreds within a number that has four or more
places to the left of the decimal separator. Standard use of
the thousand separator is specified if the format contains a
thousand separator surrounded by digit placeholders (0 or #).
Two adjacent thousand separators or a thousand separator
immediately to the left of the decimal separator (whether or
not a decimal is specified) means "scale the number by divid-
ing it by 1000, rounding as needed." For example, you can
use the format string "##0,," to represent 100 million as 100.
Numbers smaller than 1 million are displayed as 0. Two ad-
jacent thousand separators in any position other than imme-
diately to the left of the decimal separator are treated simply
as specifying the use of a thousand separator. The actual cha-
racter used as the thousand separator in the formatted output
depends on the Number Format recognized by your system.
(:) Time separator. In some locales, other characters may be
used to represent the time separator. The time separator sepa-
rates hours, minutes, and seconds when time values are for-
matted. The actual character used as the time separator in
formatted output is determined by your system settings.
(/) Date separator. In some locales, other characters may be used
to represent the date separator. The date separator separates
the day, month, and year when date values are formatted.
The actual character used as the date separator in formatted
output is determined by your system settings.
(E- E+ e-
e+)
Scientific format. If the format expression contains at least
one digit placeholder (0 or #) to the right of E-, E+, e-, or e+,
the number is displayed in scientific format and E or e is in-
serted between the number and its exponent. The number of
digit placeholders to the right determines the number of di-
gits in the exponent. Use E- or e- to place a minus sign next
to negative exponents. Use E+ or e+ to place a minus sign
next to negative exponents and a plus sign next to positive
exponents.
497
- + $ ( ) Display a literal character. To display a character other than
one of those listed, precede it with a backslash (\) or enclose
it in double quotation marks (" ").
(\) Display the next character in the format string. To display a
character that has special meaning as a literal character, pre-
cede it with a backslash (\). The backslash itself isn't dis-
played. Using a backslash is the same as enclosing the next
character in double quotation marks. To display a backslash,
use two backslashes (\\).
Examples of characters that can't be displayed as literal cha-
racters are the date-formatting and time-formatting charac-
ters (a, c, d, h, m, n, p, q, s, t, w, y, / and :), the numeric-for-
matting characters (#, 0, %, E, e, comma, and period), and
the string-formatting characters (@, &, <, >, and !).
("ABC") Display the string inside the double quotation marks (" "). To
include a string in format from within code, you must use
Chr(34) to enclose the text (34 is the character code for a
quotation mark (")).
A2.5.4 FormatCurrency
FormatCurrency(Expression[,NumDigitsAfterDecimal [,IncludeLeading-
Digit [,UseParensForNegativeNumbers [,GroupDigits]]]]) returns expres-
sion formatted as a currency value using the currency symbol defined in
the system control panel.
Argument Description
expression Required. Expression to be formatted.
NumDigitsAfterDecimal Optional. Numeric value indicating
how many places to the right of the de-
cimal point are displayed. Default va-
lue is -1, which indicates that the com-
puter's regional settings are used.
IncludeLeadingDigit Optional. Tristate constant that indi-
cates whether or not a leading zero is
displayed for fractional values. See ta-
ble A2T.6 for its valid values.
498
UseParensForNegativeNumbers Optional. Tristate constant that indi-
cates whether or not to place negative
values within parentheses. See table
A2T.6 for its valid values.
GroupDigits Optional. Tristate constant that indi-
cates whether or not numbers are
grouped using the group delimiter spe-
cified in the computer's regional set-
tings. See table A2T.6 for its valid va-
lues.
Table AT2.5 FormatCurrency’s arguments descriptions
The IncludeLeadingDigit, UseParensForNegativeNumbers, and Group-
Digits arguments have the following valid values:
Constant Value Description
vbTrue -1 True
vbFalse 0 False
vbUseDefault -2 Use the setting from the
computer's regional settings.
Table AT2.6 FormatCurrency’s arguments valid values
When one or more optional arguments are omitted, the values for omitted
arguments are provided by the computer's regional settings.
The position of the currency symbol relative to the currency value is de-
termined by the system's regional settings.
All settings information comes from the Regional Settings Currency tab,
except leading zero which comes from the Number tab.
A2.5.5 FormatDateTime
FormatDateTime(Date[,NamedFormat]) returns an expression formatted
as a date or time.
Argument Description
Date Required. Date expression to be formatted.
499
NamedFormat Optional. Numeric value that indicates the date/time for-
mat used. If omitted, vbGeneralDate is used.
NamedFormat has the following valid values:
Constant Value Description
vbGeneralDate 0 Display date and/or time. If there is a date part,
display it as a short date. If there is a time part,
display it as a long time. If present, both parts
are displayed.
vbLongDate 1 Display date using the long date format specified
in your computer's regional settings.
vbShortDate 2 Display date using the short date format speci-
fied in your computer's regional settings.
vbLongTime 3 Display time using the time format specified in
your computer's regional settings.
vbShortTime 4 Display time using the 24-hour format (hh:mm).
A2.5.6 FormatNumber
FormatNumber(expression[,NumDigitsAfterDecimal [,IncludeLeadingDi-
git [,UseParensForNegativeNumbers [,GroupDigits]]]]) returns expres-
sion formatted as a number. See table A2T.5 for its arguments’ descrip-
tions. When one or more optional arguments are omitted, the values for
omitted arguments are provided by the computer's regional settings. All
settings information comes from the Regional Settings Number tab.
A2.5.7 FormatPercent
FormatPercent(expression[,NumDigitsAfterDecimal [,IncludeLeadingDi-
git [,UseParensForNegativeNumbers [,]]]]) returns expression formatted
as a percentage (multipled by 100) with a trailing % character. See table
A2T.5 for its arguments’ descriptions. When one or more optional argu-
ments are omitted, the values for omitted arguments are provided by the
computer's regional settings. All settings information comes from the Re-
gional Settings Number tab.
500
A2.5.8 Asc, AscB, and AscW
Asc(string), AscB(string), and AscW(string) return the ASCII, binary, and
UNICODE, respectively, first character of string. The required string ar-
gument is any valid string expression. If string contains no characters, a
run-time error occurs. On SBCS systems, range for the returned values is
0 – 255; on DBCS ones, range is -32768 to 65535.
AscB is used with byte data contained in a string: instead of returning the
character code for the first character, AscB returns the first byte. AscW re-
turns the UNICODE character code, except on platforms where UNI-
CODE is not supported, in which case its behavior is identical to that of
Asc.75
A2.5.9 Chr, ChrB, and ChrW
Chr(charcode), ChrB(charcode), and ChrW(charcode) return the string
corresponding to the Long charcode ASCII, binary, and UNICODE, res-
pectively, character code.
Numbers from 0 - 31 are the same as standard, nonprintable ASCII
codes.76
On SBCS systems, range for charcode is 0 – 255; on DBCS
ones, range is -32768 to 65535.
ChrB is used with byte data contained in a String. Instead of returning a
character, which may be one or two bytes, ChrB always returns a single
byte. ChrW returns a String containing the UNICODE character, except
on platforms where UNICODE is not supported, in which case its beha-
vior is identical to that of Chr.77
A2.5.10 Hex
Hex(number) returns a String representing the hexadecimal value of num-
ber. The required number argument is any valid numeric or string expres-
sion. If number is not already an integer, before being evaluated it is
rounded to the nearest integer.
75
VB for Macintosh does not support UNICODE. Therefore, AscW(n) cannot return all
UNICODE characters for n values in the range 128 - 65,535, as it does in Windows envi-
ronments. Instead, AscW(n) attempts a "best guess" for UNICODE values n greater than
127. Therefore, you should not use AscW in the Macintosh environment. 76
For example, Chr(10) returns a linefeed character. 77
VB for Macintosh does not support UNICODE. Therefore, ChrW(n) cannot return all
UNICODE characters for n values in the range 128 - 65,535, as it does in Windows envi-
ronments. Instead, ChrW(n) attempts a "best guess" for UNICODE values n greater than
127. Therefore, you should not use ChrW in the Macintosh environment.
501
When number is: Hex returns:
Null Null
Empty 0 (zero)
Any other number Up to eight hexadecimal characters
You can represent hexadecimal numbers directly by preceding numbers in
the proper range with &H.78
A2.5.11 Oct
Oct(number) returns a Variant (String) representing the octal value of
number. The required number argument is any valid numeric or string ex-
pression. If number is not already an integer, before being evaluated it is
rounded to the nearest integer.
When number is: Oct returns:
Null Null
Empty 0 (zero)
Any other number Up to 11 octal characters
You can represent octal numbers directly by preceding numbers in the
proper range with &O.79
A2.5.12 CVErr
CVErr(errornumber) returns a Variant of subtype Error containing error-
number. The required errornumber argument is any valid error number.
Use CVErr to create user-defined errors in your procedures. For example,
if you create a function that accepts several arguments and normally re-
turns a string, you can have your function evaluate the input arguments to
ensure they are within acceptable range. If they are not, it is likely your
function will not return what you expect. In this event, CVErr allows you
to return an error number that tells you what action to take.
78 For example, &H10 represents decimal 16 in hexadecimal notation.
79 For example, &O10 is the octal notation for decimal 8.
502
Note that implicit conversion of an Error is not allowed. For example,
you can't directly assign the return value of CVErr to a variable that is not
a Variant. However, you can perform an explicit conversion (using CInt,
CDbl, and so on) of the value returned by CVErr and assign that to a va-
riable of the appropriate data type.
A2.6 Program flow control functions
A2.6.1 IIf
IIf(expr, truepart, falsepart) returns either truepart or falsepart, depend-
ing on the evaluation of expr.
Argument Description
expr Required. Expression you want to evaluate.
truepart Required. Value or expression returned if expr is True.
falsepart Required. Value or expression returned if expr is False.
Note that IIf always evaluates both truepart and falsepart, even though it
returns only one of them: because of this, you should consider possible
undesirable side effects.80
A2.6.2 Switch
Switch(expr-1, value-1[, expr-2, value-2 … [, expr-n,value-n]]) evaluates
a list of expressions and returns a Variant value or an expression associa-
ted with the first expression in the list that is True. Switch argument list
consists of n (natural, n > 0) pairs of expressions and values. The expres-
sions are evaluated from left to right, and the value associated with the
first expression to evaluate to True is returned. If the arguments aren't
properly paired, a run-time error occurs. For example, if expr-1 is True,
Switch returns value-1; if expr-1 is False, but expr-2 is True, Switch re-
turns value-2, and so on.
Switch returns Null if none of the expressions is True or the first True ex-
pression has the Null value. Note that Switch evaluates all of the expres-
sions, even though it returns only one of them: because of this, you should
consider possible undesirable side effects (see similar note 79).
80
For example, if evaluating falsepart results in a division by zero error, an error occurs
even if expr is True.
503
Argument Description
expr-i* Required. Variant expression you want to evaluate.
value-i* Required. Value or expression to be returned if the corres-
ponding expression is True.
* 1 i n
A2.6.3 Choose
Choose(index, choice-1[, choice-2, ... [, choice-n]]) selects and returns a
value from its list of arguments, based on the value of index: if index is 1,
Choose returns the first choice in the list; if index is 2, it returns the se-
cond choice, and so on (where n > 0, natural). If index is not already an
integer, before being evaluated it is rounded to the nearest integer. Choose
returns Null if index is less than 1 or greater than n.
Argument Description
index Required. Numeric expression or field that results in a value
between 1 and n.
choice-i* Required. Variant expression containing one of the possible
choices.
* 1 i n
Choose is particularly useful if index represents the value in an option
group. Note that Choose evaluates every choice in the list, even though it
returns only one: because of this, you should consider possible undesira-
ble side effects.81
A2.6.4 CallByName
CallByName(object, procname, calltype,[args()]) executes a method or
gets/sets a property of an object or set.
81
For example, if you use the MsgBox function as part of the expressions of all choices,
a message box will be displayed for each choice as it is evaluated, even though Choose
returns the value of only one of them.
504
Argument Description
object Required; Variant (Object). The name of the object/set on
which procname will be executed or whose properties will be
get/set.
procname Required; Variant (String). A string expression containing the
name of a property or method of object.
calltype Required; Constant. A constant of type vbCallType represent-
ing the type of procname (that is one of the following four:
vbGet, vbSet, vbLet, or vbMethod).
args() Optional: Variant (Array). Actual procname‘s argument va-
lues list (if any).
A2.6.5 DoEvents
DoEvents() yields execution so that the operating system can process o-
ther events and returns an Integer representing the number of open forms
in stand-alone versions of VB, such as VB Professional Edition, and zero
in all other applications.
DoEvents passes control to the operating system: control is returned after
the operating system has finished processing the events in its queue and
all keys in the SendKeys queue have been sent. DoEvents is useful for
simple tasks like allowing users to cancel a process after it has started (for
example a search for a file). For long-running processes, yielding the pro-
cessor is better accomplished by using a Timer or delegating the task to an
ActiveX EXE component. In the latter case, the task can continue comple-
tely independent of your application, and the operating system takes care
of multitasking and time slicing.
Any time you temporarily yield the processor within an event procedure,
make sure the procedure is not executed again from a different part of
your code before the first call returns, as this could cause unpredictable
results. In addition, do not use DoEvents if other applications could possi-
bly interact with your procedure in unforeseen ways during the time you
have yielded control.
505
A2.7 File functions
A2.7.1 FileAttr
FileAttr(filenumber, returntype) returns a Long representing the file mode
for files opened using the Open statement.
Argument Description
filenumber Required; Integer. Any valid file number.
returntype Required; Integer. Number indicating the type of information
to return. Specify 1 to return a value indicating the file mode.
On 16-bit systems only, specify 2 to retrieve an operating
system file handle. returntype 2 is not supported in 32-bit
systems and causes an error.
When the returntype argument is 1, the following return values indicate
the file access mode:
Mode Value
Input 1
Output 2
Random 4
Append 8
Binary 32
A2.7.2 FileDateTime
FileDateTime(pathname) returns a Variant (Date) that indicates the date
and time when the file specified by pathname was created or last modi-
fied. The pathname string expression may include the directory (folder)
and the drive.
A2.7.3 FileLen
FileLen(pathname) returns a Long specifying the length in bytes of the
file specified by pathname. The pathname string expression may include
the directory (folder) and the drive. If the specified file is open when the
FileLen function is called, the value returned represents the size of the file
immediately before it was opened.
506
To obtain the actual length of an open file use the LOF function instead.
A2.7.4 LOF
LOF(filenumber) returns a Long representing the size in bytes, of file file-
number opened using the Open statement. The required filenumber argu-
ment is an Integer containing a valid file number (handler).
To obtain the length of a file that is not open use the FileLen function.
A2.7.5 FreeFile
FreeFile[(rangenumber)] returns an Integer representing the next file
number available for use by the Open statement. The optional rangenum-
ber argument is a Variant that specifies the range from which the next
free file number is to be returned: specify 0 (default) to return a file num-
ber in the range 1 – 255; specify 1 to return a file number in the range 256
– 511. Use FreeFile to supply a file number that is not already in use.
A2.7.6 Input
Input(number, [#]filenumber) returns String containing number characters
from file filenumber opened in Input or Binary mode.
Argument Description
number Required. Any valid numeric expression specifying the num-
ber of characters to return.
filenumber Required. Any valid file number.
Data read with the Input function is usually written to a file with Print #
or Put. Use this function only with files opened in Input or Binary mode.
Unlike the Input # statement, Input returns all of the characters it reads,
including commas, carriage returns, linefeeds, quotation marks, and lead-
ing spaces.
With files opened for Binary access, an attempt to read through the file
using Input until EOF returns True generates an error: when reading
binary files with Input, use LOF and Loc instead of EOF or use Get when
using EOF.
Use the InputB function for byte data contained within text files: with In-
putB, number specifies the number of bytes to return rather than those of
characters.
507
A2.7.7 Loc
Loc(filenumber) returns a Long specifying the current read/write position
within open file filenumber. The required filenumber argument is any va-
lid Integer file number (handler). Here are the returned values for each
file access mode:
Mode Return Value
Random Number of the last record read from or written to the file.
Sequential Current byte position in the file divided by 128. However,
information returned by Loc for sequential files is neither
used nor required.
Binary Position of the last read or written byte.
A2.7.8 Seek
Seek(filenumber) returns a Long specifying the current read/write position
within file filenumber opened using the Open statement, which may be in
the range 1 to 2,147,483,647 (equivalent to 2^31 - 1). The required file-
number argument is an Integer containing a valid file number (handler).
Here are the returned values for each file access mode:
Mode Return Value
Random Number of the next read or written record.
Binary, Output,
Append, Input
Byte position at which the next operation takes place:
the 1st byte in a file is at position 1, the 2
nd is at 2, etc.
For files opened in Random mode, Seek returns the number of the next re-
cord; for those opened in other modes, it returns the byte position at
which the next operation takes place.
A2.7.9 EOF
EOF(filenumber) returns an Integer containing the Boolean value True
when the end of file filenumber opened for Random or sequential Input
has been reached. The required filenumber argument is an Integer con-
taining any valid file number (handler). With files opened for Output,
EOF always returns True. Use EOF to avoid the error generated by at-
tempting to get input past the end of a file. EOF returns False until the
end of the file has been reached.
508
With files opened for Random or Binary access, EOF returns False until
the last executed Get statement is unable to read an entire record. With
files opened for Binary access, an attempt to read through the file using
the Input function until EOF returns True generates an error: when read-
ing binary files with Input, use LOF and Loc instead of EOF or use Get
when using EOF.
A2.8 Other operating system functions
A2.8.1 Shell
Shell(pathname[,windowstyle]) runs the executable program identified by
pathname and returns a Variant (Double) representing the program's task
ID,82
if successful83
; otherwise, it returns zero. If Shell can't start path-
name an error occurs.
Argument Description
pathname Required; Variant (String). Name84
of the program to exe-
cute and any required arguments or command-line swit-
ches; may include directory (folder) and drive.
windowstyle Optional. Variant (Integer) corresponding to the style of
the window in which the program is to be run. If window-
style is omitted, the program is started minimized with fo-
cus.85
82
a number assigned by the operating systems for uniquely identifying running programs 83
On Macintosh, vbNormalFocus, vbMinimizedFocus, and vbMaximizedFocus place the
application in the foreground; vbHide, vbNoFocus, and vbMinimizeFocus place the ap-
plication in the background. 84
On the Macintosh, you can use the MacID function to specify an application's signa-
ture instead of its name. The following example uses the signature for Microsoft Word:
Shell MacID("MSWD"). 85
On the Macintosh (System 7.0 or later), windowstyle only determines whether or not
the application gets the focus when it is run.
509
The windowstyle named argument has these valid values:
Constant Value Description
vbHide 0 Window is hidden and focus is passed to
the hidden window.86
vbNormalFocus 1 Window has focus and is restored to its
original size and position.
vbMinimizedFocus 2 Window is displayed as an icon with fo-
cus.
vbMaximizedFocus 3 Window is maximized with focus.
vbNormalNoFocus 4 Window is restored to its most recent size
and position. The currently active window
remains active.
vbMinimizedNoFocus 6 Window is displayed as an icon. The cur-
rently active window remains active.
Note that, by default, Shell runs other programs asynchronously: this
means that a program started with Shell might not finish executing before
the statements following Shell are executed.
A2.8.2 CurDir
CurDir[(drive)] returns a Variant (String) representing the current path.
The optional drive argument is a string expression that specifies an exist-
ing drive. If no drive is specified or if drive is a zero-length string (""),
CurDir returns the path for the current drive.87
A2.8.3 Dir
Dir[(pathname[, attributes])] returns a String representing the name of the
file or directory (folder) identified by pathname that matches a specified
pattern or file attributes, or the volume label of a drive. You must specify
pathname the first time you call Dir or an error occurs. If you also specify
file attributes, pathname must be included too. Dir returns the first file
name that matches pathname. To get any additional file names that match
pathname, call Dir again with no arguments. When no more file names
86
The vbHide constant is not applicable on Macintosh platforms. 87
On the Macintosh, CurDir ignores any drive specified and simply returns the path for
the current drive.
510
match, Dir returns a zero-length string (""). Once a zero-length string is
returned, you must specify pathname in subsequent calls or an error oc-
curs. You can change to a new pathname without retrieving all of the file
names that match the current pathname. However, you can't call Dir re-
cursively. Calling Dir with the vbDirectory attribute does not continually
return subdirectories!
Argument Description
pathname Optional. String expression that specifies a file name (may in-
clude directory (folder) and drive). A zero-length string ("") is
returned if pathname is not found.
attributes Optional. Constant or numeric expression, whose sum speci-
fies file attributes. If omitted, returns files that match path-
name but have no attributes.
The attributes argument valid values are the following:
Constant Value Description
vbNormal 0 (Default) Specifies files with no attributes.
vbReadOnly 1 Specifies read-only files in addition to files with no
attributes.
vbHidden 2 Specifies hidden files in addition to files with no at-
tributes.
vbSystem 4 Specifies system files in addition to files with no at-
tributes.88
vbVolume 8 Specifies volume label; if any other attributed is
specified, vbVolume is ignored. See footnote 87.
vbDirectory 16 Specifies directories or folders in addition to files
with no attributes.
vbAlias 64 Specified file name is an alias. Available only on
the Macintosh.
Table AT2.7 File attribute constants
88
Not available on the Macintosh.
511
Note that in MS Windows, Dir supports the use of multiple character (*)
and single character (?) wildcards to specify multiple files.89
To iterate over all files in a folder, specify an empty string: Dir("").90
Because file names are retrieved in no particular order, you may want to
store returned file names in an array and then sort it.
A2.8.4 GetAttr
GetAttr(pathname) returns an Integer representing the attributes of file or
directory (folder) pathname. The required pathname argument is a string
expression that specifies a file name; pathname may include the directory
(folder) and the drive. Values returned by GetAttr are the sum of the attri-
bute ones from table A2T.7 above.
To determine which attributes are set, use the And operator to perform a
bitwise comparison of the value returned by GetAttr and the value of the
individual file attribute you want. If the result is not zero, that attribute is
set for the corresponding file.91
A2.8.5 Environ
Environ({envstring | number}) returns the String associated with an ope-
rating system environment variable.92
If envstring can't be found in the
environment-string table, a zero-length string ("") is returned. Otherwise,
Environ returns the text assigned to the specified envstring: that is, the
text following the equal sign (=) in the environment-string table for that
environment variable. If you specify number, the string occupying that
numeric position in the environment-string table is returned. In this case,
Environ returns all of the text, including envstring. If there is no environ-
ment string in the specified position, Environ returns a zero-length string.
89 On the Macintosh, these characters are treated as valid file name characters and can't
be used as wildcards to specify multiple files. Since the Macintosh doesn't support the
wildcards, use the file type to identify groups of files. You can use the MacID function
to specify file type instead of using the file names. For example, the following statement
returns the name of the first TEXT file in the current folder: Dir("SomePath",
MacID("TEXT")). 90
If you use the MacID function with Dir in MS Windows, an error occurs. Any attri-
bute value greater than 256 is considered a MacID one. 91
For example, the return value of the following And expression is zero if the Archive at-
tribute is not set (whereas a nonzero value is returned if Archive is set): Result =
GetAttr(FName) And vbArchive. 92
Not available on the Macintosh.
512
Argument Description
envstring Optional. String expression containing the name of an envi-
ronment variable.
number Optional. Numeric expression corresponding to the numeric
order of the environment string in the environment-string ta-
ble. The number argument can be any numeric expression, but
is rounded to an integer before it is evaluated.
A2.8.6 GetAllSettings
GetAllSettings(appname, section) returns a list of key settings and their
respective values (originally created with SaveSetting) from an applica-
tion's entry in the Windows registry93
or an uninitialized Variant if either
appname or section does not exist.
Argument Description
appname Required. String expression containing the name of the appli-
cation or project whose key settings are requested.94
section Required. String expression containing the name of the sec-
tion whose key settings are requested. GetAllSettings returns a
Variant whose contents is a two-dimensional array of strings
containing all the key settings in the specified section and
their corresponding values.
A2.8.7 GetSetting
GetSetting(appname, section, key[, default]) returns the key setting value
from the appname application's entry in the Windows registry (for Macin-
tosh see footnote 92). If any of its arguments do not exist, GetSetting re-
turns the value of default.
Argument Description
appname Required. String expression containing the name of the appli-
cation or project whose key setting is requested (for Macin-
tosh see footnote 93).
93
or, on the Macintosh, information in the application's initialization file. 94
On the Macintosh, this is the filename of the initialization file in the Preferences fol-
der in the System folder.
513
section Required. String expression containing the name of the sec-
tion where the key setting is found.
key Required. String expression containing the name of the key
setting to return.
default Optional. Expression containing the value to return if no value
is set in the key setting. If omitted, default is assumed to be a
zero-length string ("").
A2.8.8 Spc
Spc(n) is used with the Print # statement or the Print method to position
output. The required n argument is the number of spaces to insert before
displaying or printing the next expression in a list. If n is less than the out-
put line width, the next print position immediately follows the number of
spaces printed; if n is greater than the output line width, Spc calculates the
next print position using the formula: currentprintposition + (n Mod
width)95
; if the difference between the current print position and the out-
put line width is less than n (or n Mod width), Spc skips to the beginning
of the next line and generates spaces equal to n - (width – currentprintpo-
sition). Make sure your tabular columns are wide enough to accommodate
wide letters.
When you use the Print method with a proportionally spaced font, the
width of space characters printed using Spc is always an average of the
width of all characters in the point size for the chosen font. However,
there is no correlation between the number of characters printed and the
number of fixed-width columns those characters occupy.96
A2.8.9 Tab
Tab[(n)] is used with the Print # statement or the Print method to position
output. The optional n argument is the column number moved to before
displaying or printing the next expression in a list; if omitted, Tab moves
the insertion point to the beginning of the next print zone: this allows Tab
to be used instead of a comma in locales where the comma is used as a
decimal separator. If the current print position on the current line is grea-
95
For example, if the current print position is 24, the output line width is 80, and you
specify Spc(90), the next print will start at position 34 (current print position + the re-
mainder of 90/80). 96
For example, the uppercase letter W occupies more than one fixed-width column and
the lowercase letter i occupies less than one fixed-width column.
514
ter than n, Tab skips to the nth column on the next output line; if n is less
than 1, Tab moves the print position to column 1; if n is greater than the
output line width, Tab calculates the next print position using the formula:
n Mod width;97
if n is less than the current print position, printing begins
on the next line at the calculated print position; if the calculated print po-
sition is greater than the current print position, printing begins at the
calculated print position on the same line.
Note that the leftmost print position on an output line is always 1. When
you use the Print # statement to print to files, the rightmost print position
is the current width of the output file, which you can set using the Width #
statement.
Make sure your tabular columns are wide enough to accommodate wide
letters. When you use Tab with the Print method, the print surface is divi-
ded into uniform, fixed-width columns. The width of each column is an
average of the width of all characters in the point size for the chosen font.
However, there is no correlation between the number of characters printed
and the number of fixed-width columns those characters occupy (see foot-
note 95).
A2.9 Array functions
A2.9.1 Array
Array(arglist) returns a Variant containing an array. The required arglist
argument is a comma-delimited list of values that are assigned to the ele-
ments of the array contained within the Variant. If no arguments are spe-
cified, an array of zero length is created.
The notation used to refer to an element of an array consists of the varia-
ble name followed by parentheses containing an index number indicating
the desired element.98
The lower bound of an array created using Array is determined by the
lower bound specified with the Option Base statement, unless Array is
97
For example, if width is 80 and you specify Tab(90), the next print will start at column
10 (the remainder of 90/80). 98
For example: Dim A As Variant ‘creates a Variant variable named A
A = Array(10,20,30)‘assigns an array to variable A
B = A(2) ‘assigns the value contained in the second array
‘element to another variable
515
qualified with the name of the type library (for example VBA.Array); if
qualified with the type-library name, Array is unaffected by Option Base.
Note that a Variant that is not declared as an array can still contain an
array: a Variant variable can contain an array of any type, except fixed-
length strings and user-defined types; although a Variant containing an
array is conceptually different from an array whose elements are of type
Variant, the array elements are accessed in the same way.
A2.9.2 Filter
Filter(sourcesrray, match[, include[, compare]]) returns a zero-based ar-
ray containing the match subset of the string array sourcesrray based on a
specified filter criteria.
Argument Description
sourcearray Required. One-dimensional array of strings to be searched.
match Required. String to search for.
include Optional. Boolean value indicating whether to return sub-
strings that include or exclude match; if include is True,
Filter returns the subset of the array that contains match as
a substring; if include is False, Filter returns the subset of
the array that does not contain match as a substring.
compare Optional. Numeric value indicating the kind of string com-
parison to use. See table A2.T1 for its valid values.
The compare argument can have the following values:
Constant Value Description
vbUseCompareOption -1 Performs a comparison using the setting
of the Option Compare statement.
vbBinaryCompare 0 Performs a binary comparison.
vbTextCompare 1 Performs a textual comparison.
vbDatabaseCompare 2 MS Access only. Performs a comparison
based on information in your database.
516
If no matches of match are found within sourcearray, Filter returns an
empty array. An error occurs if sourcearray is Null or is not a one-dimen-
sional array. The array returned by the Filter function contains only e-
nough elements to contain the number of matched items.
A2.9.3 LBound
LBound(arrayname[, dimension]) returns a Long containing the smallest
available subscript for the indicated dimension of the array arrayname.
Argument Description
arrayname Required. Name of the array variable; follows standard va-
riable naming conventions.
dimension Optional; Variant (Long). Natural indicating which dimen-
sion's lower bound is returned: use 1 for the first dimension,
2 for the second, etc.; if dimension is omitted, 1 is assumed.
LBound is used in conjunction with UBound (which returns upper limits
of arrays) to determine the size of arrays.
LBound returns the values in the following table for an array with the fol-
lowing dimensions:
The default lower bound for any dimension is either 0 or 1, depending on
the setting of the Option Base statement. However, the base of an array
created with Array is zero: it is unaffected by Option Base.
Arrays for which dimensions are set using the To clause in a Dim, Pri-
vate, Public, ReDim, or Static statement can have any integer value as a
lower bound.
A2.9.4 UBound
UBound(arrayname[, dimension]) returns a Long containing the largest
available subscript for the indicated dimension of the array arrayname.
Statement Return Value
LBound(A, 1) 1
LBound(A, 2) 0
LBound(A, 3) -3
517
Argument Description
arrayname Required. Name of the array variable; follows standard va-
riable naming conventions.
dimension Optional; Variant (Long). Natural indicating which dimen-
sion's upper bound is returned: use 1 for the first dimension,
2 for the second, and so on; if dimension is omitted, 1 is as-
sumed.
UBound is used in conjunction with LBound (which returns lower limits
of arrays) to determine the size of arrays.
UBound returns the values in the following table for an array with the fol-
lowing dimensions:
Statement Return Value
UBound(A, 1) 100
UBound(A, 2) 3
UBound(A, 3) 4
A2.10 Miscellanea functions
A2.10.1 Error
Error[(errornumber)] returns the error message that corresponds to error-
number. The optional errornumber argument can be any valid error num-
ber. If errornumber is a valid error number, but is not defined, Error re-
turns the string "Application-defined or object-defined error." If error-
number is not valid, an error occurs. If errornumber is omitted, the mes-
sage corresponding to the most recent run-time error is returned. If no
run-time error has occurred, or errornumber is 0, Error returns a zero-
length string (""). Examine the property settings of the Err object to iden-
tify the most recent run-time error. The return value of the Error function
corresponds to the Description property of the Err object.
A2.10.2 QBColor
QBColor(color) returns a Long representing the RGB color code corres-
ponding to the color number. The required color argument is a natural in
the range 0-15.
518
The color argument has the following valid values:
Number Color Number Color
0 Black 8 Gray
1 Blue 9 Light Blue
2 Green 10 Light Green
3 Cyan 11 Light Cyan
4 Red 12 Light Red
5 Magenta 13 Light Magenta
6 Yellow 14 Light Yellow
7 White 15 Bright White
Note that the color argument represents color values used by earlier ver-
sions of Basic (such as MS VB for MS-DOS and the Basic Compiler).
Starting with the least-significant byte, the returned value specifies the
red, green, and blue values used to set the appropriate color in the RGB
system used by VBA.
A2.10.3 RGB
RGB(red, green, blue) returns a Long natural representing an RGB color
value obtained from mixing different nuances of red, green, and blue.99
Argument Description
red Required; Variant (Integer). Number in the range 0-255 that
represents the red component of the color.
green Required; Variant (Integer). Number in the range 0-255 that
represents the green component of the color.
blue Required; Variant (Integer). Number in the range 0-255 that
represents the blue component of the color.
99
The RGB color values returned by this function are incompatible with those used by
the Macintosh operating system. They may be used within the context of Microsoft ap-
plications for the Macintosh, but should not be used when communicating color changes
directly to the Macintosh operating system.
519
Application methods and properties that accept a color specification ex-
pect that specification to be a number representing an RGB color value.
An RGB color value specifies the relative intensity of red, green, and blue
to cause a specific color to be displayed. The value for any argument to
RGB that exceeds 255 is assumed to be 255.
The following table lists some standard colors and the red, green, and
blue values they include:
Color Red Value Green Value Blue Value
Black 0 0 0
Blue 0 0 255
Green 0 255 0
Cyan 0 255 255
Red 255 0 0
Magenta 255 0 255
Yellow 255 255 0
White 255 255 255
A2.10.4 CreateObject
CreateObject(class,[servername]) creates and returns a reference to an
ActiveX object named class. Note that every application that supports Au-
tomation provides at least one type of object. For example, a word proces-
sing application may provide an Application object, a Document object,
and a Toolbar object. To create an ActiveX object, assign the object re-
turned by CreateObject to an object variable.
Argument Description
Class Required; Variant (String). The application name and class
of the object to create.
servername Optional; Variant (String). The name of the network server
where the object will be created. If servername is an empty
string (""), the local machine is used.
520
The class argument uses the syntax appname.objecttype and has these
components:
Component Description
appname Required; Variant (String). The name of the application pro-
viding the object.
objecttype Required; Variant (String). The type or class of object to
create.
A2.10.5 GetObject
GetObject([pathname] [, class]) returns a reference to an object provided
by an ActiveX component and identified by pathname or class; if path-
name is a zero-length string (""), GetObject returns a new object instance
of the specified type; if the pathname argument is omitted, GetObject re-
turns a currently active object of the specified type. If no object of the
specified type exists, an error occurs.
Argument Description
pathname Optional; Variant (String). The full path and name of the file
containing the object to retrieve. If pathname is omitted, class
is required.
class Optional; Variant (String). A string representing the object
class name.
class uses the syntax appname.objecttype, where:
Part Description
appname Required; Variant (String). The name of the application pro-
viding the object.
objecttype Required; Variant (String). The type or class of object to re-
trieve.
Use GetObject when there is a current instance of the object or if you
want to create the object with a file already loaded. If there is no current
instance and you don't want the object started with a file loaded, use the
CreateObject function instead.
521
GetObject is used to access an ActiveX object from a file and assign the
object to an object variable. Use the Set statement to assign the object re-
turned by GetObject to the object variable.100
Some applications allow you to activate part of a file. Add an exclamation
point (!) to the end of the file name and follow it with a string that identi-
fies the part of the file you want to activate. For information on how to
create this string, see the documentation for the application that created
the object.101
If you don't specify the object's class, Automation determines the applica-
tion to start and the object to activate, based on the file name you provide.
Some files, however, may support more than one class of object. For
example, a drawing might support three different types of objects: an Ap-
plication object, a Drawing object, and a Toolbar object, all of which are
part of the same file. To specify which object in a file you want to
activate, use the optional class argument.102
Once an object is activated, you reference it in code using the object va-
riable you defined. In the preceding example, you access properties and
methods of the new object using the object variable MyObject.103
If an object has registered itself as a single-instance object, only one in-
stance of the object is created, no matter how many times CreateObject is
executed. With a single-instance object, GetObject always returns the
same instance when called with the zero-length string ("") syntax, and it
100
For example, when the following code is executed, the application associated with the
specified pathname is started and the object in the specified file is activated: Dim CADObject As Object
Set CADObject = GetObject("C:\CAD\SCHEMA.CAD") 101
For example, in a drawing application you might have multiple layers to a drawing
stored in a file. You could use the following code to activate a layer within a drawing
called SCHEMA.CAD: Set LayerObject = GetObject("C:\CAD\SCHEMA.CAD!Layer3") 102
For example, if FIGMENT is the name of a drawing application and DRAWING is
one of the object types it supports: Dim MyObject As Object
Set MyObject = GetObject("C:\DRAWINGS\SAMPLE.DRW",
"FIGMENT.DRAWING") 103
For example: MyObject.Line 9, 90
MyObject.InsertText 9, 100, "Hello, world."
MyObject.SaveAs "C:\DRAWINGS\SAMPLE.DRW"
522
causes an error if the pathname argument is omitted. Note that you can't
use GetObject to obtain a reference to a class created with VB.
A2.10.6 Financial functions
Here are the financial functions (all returning a corresponding Double):
Function Result
DDB The depreciation of an asset for a specific time period using
the double-declining balance or some other specified method.
FV The future value of an annuity based on periodic, fixed pay-
ments, and a fixed interest rate.
IPmt The interest payment for a given period of an annuity based
on periodic, fixed payments, and a fixed interest rate.
IRR The internal rate of return for a series of periodic cash flows
(payments and receipts).
MIRR The modified internal rate of return for a series of periodic
cash flows (payments and receipts).
NPer The number of periods for an annuity based on periodic,
fixed payments, and a fixed interest rate.
NPV The net present value of an investment based on a series of
periodic cash flows (payments and receipts) and a discount
rate.
Pmt The payment for an annuity based on periodic, fixed pay-
ments, and a fixed interest rate.
PPmt The principal payment for a given period of an annuity based
on periodic, fixed payments, and a fixed interest rate.
PV The present value of an annuity based on periodic, fixed pay-
ments to be paid in the future, and a fixed interest rate.
Rate The interest rate per period for an annuity.
SLN The straight-line depreciation of an asset for a single period.
SYD The sum-of-years' digits depreciation of an asset for a speci-
fied period.
523
Subject Index - 497 ! 491, 521 " 497 # 495
See one-to-one function symbol $ 497 % 495, See Oracle wildcard
character & 491, See Access concatenation
operator, See string concatenation operator
( ) 497 (/) 492 (:)492 (co-)domain constraint ................ 19 (conventional) commutative
function diagram example ................................. 287
* 511, See Access wildcard
character , 496 . 495 / 496 : 496 ? 511 @ 491 || See Oracle concatenation
operator + 497 < 491 > 491 0 495 a/p .............................................. 494 A/P ............................................. 494 aaaa ........................................... 492 ABC ........................................... 497 Abs ............................................ 479 Access
Report Generator and Manager ........................................... 322
Report Layout Tools .............. 322 Report Wizard ....................... 322
ActiveX object ............................ 519, 521
ActiveX EXE component .......... 504 acyclic autofunction
examples ....................... 201, 221 acyclic constraint
example ................................. 282 ADF .. See Application Development
Framework ADO ........ See ActiveX Data Objects ADODB
connection ............................. 309 recordset ................................ 310
AfterDelConfirm form event ..... 300 AfterInsert form event ............... 299 AfterUpdate
control event ......................... 301 form event ............................. 299
Algorithm A0 DML statements design
assistance ........................... 120 Query design assistance ........ 120
am/pm ........................................ 494 AM/PM ...................................... 494 AMPM ....................................... 494 And ............................................ 511 anti-commutative constraint
example ................................. 287 anti-symmetric constraint
example ................................. 284 antisymmetric math relation ..... 158,
171 anti-transitive constraint
example ................................. 284 Append .............................. 505, 507 Application ........................ 519, 521 Arccos ....................................... 482 Arccosec .................................... 482 Arccotan .................................... 482
524
Arcsec ........................................ 482 Arcsin ........................................ 482 ARGM ................ See Access Report
Generator and Manager ARLT ..... See Access Report Layout
Tools Array .......................................... 514 ARW ....... See Access Report Wizard Asc ............................................. 500 AscB .......................................... 500 ASCII(l) ....................................... 14 AscW ......................................... 500 Atn ............................................. 482 attribute co-domain.... See attribute
domain, See attribute domain, See attribute domain, See attribute domain, See attribute domain, See attribute domain, See attribute domain, See attribute domain
attribute domain example ......... 249, 250, 255, 267
attribute range ............ See attribute domain, See attribute domain, See attribute domain, See attribute domain, See attribute domain, See attribute domain, See attribute domain, See attribute domain
Automation ........................ 519, 521 BA ................. See business analysis BeforeDelConfirm form event .. 300 BeforeInsert form event ............. 298 BeforeUpdate
control event .......................... 301 form event ............................. 299
Binary ................ 505, 506, 507, 508 Black .......................................... 519 Blue ........................................... 519 BOF ...................... See Begin Of File Boolean ...................................... 466 BPR1.0
tables should correspond to
object sets ............................ 74 BPR1.1
ordinary columns should
correspond to object set
properties ............................. 74 BPR1.2
foreign keys should correspond
to functions defined on and
taking values from object sets
............................................. 74 BPR1.3
when available, always use
comboboxes for all foreign
keys ..................................... 74 BPR1.4
at least one column per table not
accepting nulls ..................... 74 BPR1.5
always ban dirty nulls .............. 74 BPR1.6
at least one semantic key per
table ..................................... 74 BPR1.7
use GUI for increasing
productivity ......................... 75 BPR2.0
backup instances before
modifying them ................. 117 BPR2.1
first test UPDATE and DELETE
SELECTs........................... 117 BPR2.2
modify only data which has to be
modified ............................ 117 BPR2.3
use rollback transactions ....... 117 BPR2.4
caution with the ON DELETE
CASCADE ........................ 117 BPR2.5
purge immediately old unneeded
data .................................... 117
525
BPR2.6 factorize I/Os ......................... 117
BPR2.7 always test SQL statements ... 118
BPR3.0 ...................................... 156 Byte ............................................ 466 c 492 Calendar .................................... 469 CallByName .............................. 503 Cancel ........................................ 303 cardinality
example ......................... 249, 250 carriage return............................ 463 CBool ........................................ 484 CByte ......................................... 484 CCur .......................................... 484 CDate ......................................... 484 CDbl .......................... 484, 487, 502 CDec .......................................... 484 Choose ....................................... 503 Chr ............................................. 500 ChrB .......................................... 500 ChrW ......................................... 500 CInt .................................... 485, 502 Click
control event ......................... 302 form event ............................. 300
CLng .......................................... 485 CLngLng ................................... 485 CLngPtr ..................................... 485 Close form/report event ............. 301 Combo Box .................................. 28 compulsory data
example . 249, 250, 251, 255, 267 Cos ............................................. 482 Cosec ......................................... 482 Cotan ......................................... 482 CreateObject .............. 519, 520, 521 Crystal Reports .......................... 322 CSng .......................................... 485 CStr .................................... 485, 486 CurDir ........................................ 509
Currency ............................ 466, 490 Current form/report event ......... 298 CVar .......................................... 485 CVDate ...................................... 486 CVErr ................................ 469, 501 Cyan .......................................... 519 d 470, 492 dangling pointer .................... 26, 66 DAO ......... See Data Access Objects Data Definition Language
SQL .......................................... 13 Data Manipulation Language
SQL .......................................... 13 DataErr ...................................... 304 Date ................................... 466, 469 Date$ ......................................... 469 DateAdd .................................... 474 DateDiff .................................... 475 DatePart ..................................... 470 DateSerial .................................. 472 DateValue .................................. 473 DAvg ......................................... 313 Day ............................................ 476 db ................................. See database DBCS .... See double-byte character
set DblClick
control event ......................... 302 form event ............................. 300
DCount ...................................... 313 dd ............................................... 492 DDB .......................................... 522 ddd ............................................. 492 dddd ........................................... 492 ddddd ......................................... 492 dddddd ....................................... 492 DDL . See Data Definition Language Decimal ..................................... 466 Default Value ............................ 437 Delete form event ...................... 299 Description ................................ 437 Dim ............................................ 304
526
Dir .............................................. 509 dirty null ...................................... 17 DISTINCT SQL predicate . 165, 181 DLookup .............................. 90, 312 DMax ......................................... 313 DMin ......................................... 313 DML ........... See Data Manipulation
Language DoCmd ...................................... 312 DoCmd.RunSQL ....................... 312 Document .................................. 519 domain constraint ........ (co-)domain
constraint Double ....................................... 466 Drawing ..................................... 521 DROP SQL statement ................. 13 drop-down list.......... See combo box DSum ......................................... 313 DUAL Oracle system table ....... 179 e- 496 E- 496 e.g. See for example (from the Latin
exempli gratia), See for example (from the Latin exempli gratia)
e+ .............................................. 496 E+ .............................................. 496 Empty ......................................... 466 empty recordset ......................... 310 empty set ........................... 201, 221 Enter control event .................... 301 Environ ...................................... 511 EOF ......... 506, 507, See End Of File E-RD
example ......................... 247, 248 Err ...................................... 306, 517 Error .......................... 466, 501, 517 Error form/report event ............. 300 event-driven methods ................ 303 Execute ...................................... 312 Exp ............................................ 482 FE ............. See forward engineering FileAttr ...................................... 505
FileDateTime............................. 505 FileLen .............................. 505, 506 Filter .......................................... 515 Fix ..................................... 479, 485 Fixed .......................................... 490 foreign key .................................. 14 form feed ................................... 463 Form Wizard ............................. 292 Format ............................... 486, 487 FormatCurrency ........................ 497 FormatDateTime ....................... 498 FormatNumber .......................... 499 FormatPercent ........................... 499 Forms ADO collection ............... 311 FreeFile ..................................... 506 function
kernel ..................................... 119 nucleus .......... See function kernel
FV .............................................. 522 General Date ............................. 489 General Number ........................ 490 generalized commutativity
example ................................. 289 Get ..................................... 506, 508 GetAllSettings ........................... 512 GetAttr ...................................... 511 GetObject .................................. 520 GetSetting .................................. 512 Green ......................................... 519 GUI ....... See Graphic User Interface h 470, 493 HArccos .................................... 483 HArccosec ................................. 483 HArccotan ................................. 483 HArcsec ..................................... 483 HArcsin ..................................... 483 HArctan ..................................... 483 HCos .......................................... 483 HCosec ...................................... 483 HCotan ...................................... 483 Hex ............................................ 500 Hh .............................................. 493
527
horizontal tab ............................. 463 Hour ........................................... 478 HSec .......................................... 483 HSin ........................................... 483 HTan .......................................... 483 i.e. ....... See that is (from the Latin id
est), See that is (from the Latin id est)
IIf502 Input .................. 505, 506, 507, 508 Input # ....................................... 506 InputB ........................................ 506 InputBox .................................... 309 InStr ........................................... 459 InStrB ........................................ 460 InstrRev ..................................... 460 Int ...................................... 479, 485 Integer ....................................... 466 IPmt ........................................... 522 IRR ............................................ 522 irreflexive autofunction
examples........................ 201, 221 irreflexive constraint
example ......................... 283, 284 irreflexive math relation .... 158, 171 IsArray ....................................... 468 IsDate ................................ 468, 485 IsEmpty ..................................... 466 IsError ........................................ 469 IsMissing ................................... 468 IsNull ......................................... 465 IsNull predicate
Access ............................ 164, 181 IsNumeric .................................. 468 IsObject ..................................... 469 Join ............................................ 464 ker ..................... See function kernel
(f g) .................................... 119 (f) 119
key Access constraint ............... 17, 54
Label Wizard ............................. 322
LBound ...................................... 516 LCase ........................................ 458 LCID ........................... See LocaleID Left ............................................ 458 LeftB ......................................... 459 Left-Root-Right traversal .. 193, 213 Len ............................................ 458 LenB .......................................... 458 linefeed ...................................... 463 Load form/report event ............. 298 Loc ............................. 506, 507, 508 local commutativity
example ................................. 288 LocaleID .................................... 459 Locked ....................................... 297 LOF ................................... 506, 508 Log ............................................ 482 LogN ......................................... 483 Long .......................................... 466 Long Date .................................. 489 Long Time ................................. 489 LTrim ........................................ 459 m 470, 493 MacID ....................................... 511 Magenta ..................................... 519 Me .............................................. 312 Medium Date ............................. 489 Medium Time ............................. 489 metacatalog
Access .................................... 252 Oracle .................................... 255
metacatalogue ............................ 268 metadata
example ................................. 265 Minute ....................................... 478 MIRR ........................................ 522 mm ............................................. 493 mmm .......................................... 493 mmmm ....................................... 493 Month ........................................ 477 MonthName............................... 478 MoveNext .................................. 311
528
MS ............................. See Microsoft MS Design View ................ See QBE MsgBox ............................. 307, 503 n 470 N 493 NAT(n) ........................................ 14 NewData .................................... 304 NewRecord ........................ 302, 318 Nn .............................................. 493 No Data ..................................... 331 NoData report event .................. 301 Not In List combo-box event ..... 302 not null
Access constraint ... 17, 18, 54, 55 Nothing .............................. 466, 469 Now ........................................... 469 NPer ........................................... 522 NPV ........................................... 522 Null .................................... 463, 466 null string ...... See zero-length string Object ........................................ 466 Oct ............................................. 501 OldValue .................................... 302 On Error
GoTo ...................................... 306 Resume Next ......................... 307
On/Off ....................................... 490 one-to-one function
example ................................... 14 oooo ........................................... 493 Open form/report event ............. 298 Option Base ............................... 514 Option Compare ................ 459, 515 Oracle
(co-)domain constraint ............ 56 Application Development
Framework ........................ 102 autonumber ....................... 53, 59 db connection .......................... 50 dirty null .................................. 54 domain constraint .... (co-)domain
constraint
NOT NULL constraint ............ 53 PL/SQL
EXECUTE statement ........... 117 Primary Key constraint ........... 53 semantic key ............................ 54 sequence ................................. 59 SQL Developer........................ 49 stored function ...................... 175 trigger ...................................... 60
Output ................................ 505, 507 ParamArray ............................... 468 Parent ........................................ 312 Partition ..................................... 479 Percent ....................................... 490 PL/SQL ......................................... 61
package ................................. 150 body ................................... 151 header ............................... 151
procedure .............................. 150 Pmt ............................................ 522 PPmt .......................................... 522 Primary Key ................................ 16 Print ........................................... 513 Print # ................................ 506, 513 Private ....................................... 305 Public ........................................ 305 Put ............................................. 506 PV .............................................. 522 q 470, 493 QBColor .................................... 517 QBE ............. See Query-by-Example queries hierarchy
example ......................... 204, 222 Query-by-Example ...................... 15 RA ................. See relational algebra Random ............................. 505, 507 Randomize ................................. 481 Rate ........................................... 522 RE .............. See reverse engineering Red ............................................ 519 relational constraint types ......... 292 Replace ...................................... 461
529
Report Properties ............... 330, 331 Reporting Services for SQL Server
............................................... 322 Response .................................... 304 RGB ......... 518, See Red-Green-Blue Right .......................................... 458 RightB ........................................ 459 Rnd ............................................ 481 Round ........................................ 480 RTrim ........................................ 459 s 470 S 493 SaveSetting ................................ 512 SBCS . See single-byte character set Scientific .................................... 490 Sec ............................................. 482 Second ....................................... 478 Seek ........................................... 507 SELECT SQL statement
aggregate functions ............... 119 composition ....................... 119
clause FROM ......................... 119, 120
ON sub-clause ................ 119 GROUP BY ................ 119, 120
golden rule ..................... 120 ORDER BY........................... 119 SELECT ............................... 120
AS operator .................... 156 WHERE ............................... 119
self-join .............. 189, 198, 208, 216 SendKeys ................................... 504 Sequential .................................. 507 Set .............................................. 521 Sgn ............................................. 480 Shell ........................................... 508 Short Date ................................. 489 Short Time ................................. 489 Sin .............................................. 482 Single ......................................... 466 SLN ........................................... 522 space .......................................... 463
Space ......................................... 462 Spc ............................................. 513 Split ........................................... 465 SQLSee Structured Query Language
ANSI 92 .................................... 13 ANSI 99 .................................... 13
Sqr ............................................. 482 Ss 493 SSN .......... See (U.S.) Social Security
Number Standard .................................... 490 Status ......................................... 304 Str ...................................... 486, 487 StrComp .................................... 462 StrConv ..................................... 463 String ................................. 462, 466 StrReverse ................................. 459 structural E-RD ......................... 249 structural function ....................... 14 subquery ............................ 130, 145
examples ....................... 204, 222 surrogate key ......................... 15, 53 Switch ........................................ 502 SYD ........................................... 522 t t t t t ......................................... 493 Tab ............................................ 513 Tan ............................................ 482 Time .......................................... 469 Time$ ........................................ 469 Timer ......................................... 504 TimeSerial ................................. 473 TimeValue ................................. 474 Toolbar .............................. 519, 521 transitive closure 193, 211, 225, 226
example ......................... 202, 220 transitive constraint
example ......................... 283, 284 Trim ........................................... 459 True/False .................................. 490 TypeName ................................. 466 UBound ..................................... 516 UNION ALL operator 164, 180
530
UNION operator ................. 164 uniqueness
example . 249, 250, 251, 255, 267 Unknown .................................... 466 Val ..................................... 485, 486 Variant ....................................... 305 VarType ............................. 467, 469 VBA ................. See Visual Basic for
Applications, See Visual Basic
for Applications, See Visual Basic for Applications
VBA.Array ................................ 515 vbAbortRetryIgnore .................. 308 vbAlias ...................................... 510 vbArray ...................................... 467 vbBinaryCompare ............. 459, 515 vbBoolean .................................. 467 vbByte ....................................... 467 vbCallType ................................ 504 vbCancel .................................... 308 vbCritical ................................... 307 vbCurrency ................................ 467 vbDatabaseCompare .......... 459, 515 vbDataObject ............................. 467 vbDate ....................................... 467 vbDecimal ................................. 467 vbDefaultButton1 ...................... 308 vbDefaultButton2 ...................... 308 vbDefaultButton3 ...................... 308 vbDefaultButton4 ...................... 308 vbDirectory ................................ 510 vbDouble ................................... 467 vbEmpty .................................... 467 vbError ...................................... 467 vbExclamation ........................... 307 vbFalse ...................................... 498 vbFirstFourDays ........................ 471 vbFirstFullWeek ........................ 471 vbFirstJan1 ................................ 471 vbFriday .................................... 471 vbFromUnicode ......................... 464 vbGeneralDate .......................... 499
vbGet ......................................... 504 vbHidden ................................... 510 vbHide ....................................... 509 vbHiragana ................................ 464 vbInfo ........................................ 307 vbInteger ................................... 467 vbKatakana ................................ 464 vbLet ......................................... 504 vbLong ...................................... 467 vbLongDate ............................... 499 vbLongLong .............................. 467 vbLongTime .............................. 499 vbLowerCase............................. 464 vbMaximizedFocus ................... 509 vbMethod .................................. 504 vbMinimizedFocus .................... 509 vbMinimizedNoFocus ............... 509 vbMonday ................................. 471 vbMsgBoxHelpButton .............. 307 vbNarrow .................................. 464 vbNo .......................................... 308 vbNormal .................................. 510 vbNormalFocus ......................... 509 vbNormalNoFocus .................... 509 vbNull ........................................ 467 vbObject ............................ 467, 469 vbOk .......................................... 308 vbOkCancel ............................... 307 vbOkOnly .................................. 307 vbProperCase ............................ 464 vbQuestion ................................ 307 vbReadOnly............................... 510 vbRetryCancel ........................... 308 vbSaturday ................................ 471 vbSet .......................................... 504 vbShortDate............................... 499 vbShortTime .............................. 499 vbSingle .................................... 467 vbString ..................................... 467 vbSunday ................................... 471 vbSystem ................................... 510 vbTextCompare ................. 459, 515
531
vbThursday ................................ 471 vbTrue ....................................... 498 vbTuesday ................................. 471 vbUnicode ................................. 464 vbUpperCase ............................. 464 vbUseCompareOption ....... 459, 515 vbUseDefault ............................. 498 vbUserDefinedType .................. 467 vbUseSystem ............................. 471 vbVariant ................................... 467 vbVolume .................................. 510 vbWednesday ............................ 471 vbWide ...................................... 464 vbYes ......................................... 308 vbYesNo .................................... 308
vbYesNoCancel......................... 308 vertical tab ................................. 463 w 470, 492 Weekday .................................... 477 WeekdayName .......................... 477 White ......................................... 519 Width # ...................................... 514 ww ..................................... 470, 493 y 470, 493 Year ........................................... 478 Yellow ....................................... 519 Yes/No ...................................... 490 yy 493 yyyy .................................... 470, 493