SBQL - Examples for the AS0
Store Model
Back to Description
of SBA and SBQL.
In Fig.30 we present a relational and an equivalent
object-oriented schema that will be used in our examples addressing the AS0
model. The schemata are basically self-explanatory. In the relational schema
arrows with dashed lines denote primary-foreign keys dependencies; an arrow is
leading from a foreign key to the corresponding primary key. The
object-oriented schema is presented in the UML-like syntactic fashion, with two
differences. First, cardinalities are put after each defined entity (default
cardinality 1..1 is omitted). For instance, cardinalities [0..1] after Address denote that for a particular
employee the address can occur or not. Note that such information cannot be
expressed in the relational schema. A department can be located in one or more
locations (this information also cannot be expressed in the relational schema).
Second, we represent AS0 pointer objects associated with particular objects,
thus for instance, worksIn pointer
objects are close to Emp objects. In
UML the worksIn role of the
association would be close to the Dept
class, just otherwise to our convention. Our examples will address the
object-oriented schema. If we will address the relational schema we say that
explicitly.

Fig.30. Relational schema and an
equivalent object schema used in examples
|
E.AS0.1 |
Get full information on employees. |
|
SBQL: |
Emp |
In SQL (see the relational schema) and in OQL
this query can be formulated as:
|
SQL: OQL: |
select * from Emp |
|
OQL: |
Emp |
We consider the SQL syntactic sugar unnecessary
and contradictory to the tradition of programming languages.
“User-friendliness” of this sugar is a false stereotype, especially
for complex nested queries. Note also the semantic difference: the SBQL query
returns a bag of references to all Emp
objects, while the SQL query returns the entire Emp table. The OQL query returns in this case the set of Emp objects; a reference to an object is
not a feature of the ODMG standard. SBQL queries never return objects but
references to objects, perhaps within some more complex structures.
|
E.AS0.2 |
Get full information on names of employees. |
|
SBQL: |
Emp.name |
In SQL (see the relational schema) and in OQL
this query can be formulated as:
|
SQL: OQL: |
select name from Emp |
The SBQL query returns a bag of references to
all name subobjects within Emp objects, while the SQL query returns
an one-column table with values of the names. The OQL query returns a bag of
literals, i.e. strings. OQL does not allow for the query Emp.name; the explanation of this restriction is at least strange.
Because the SBQL in this case returns references to name subobjects, it is much
more universal, for instance, can be an argument of updating statements. SQL
and OQL by definition reject such a possibility.
|
E.AS0.3 |
Get all the information on the employee named
“Doe”. |
|
SBQL: |
Emp where name = “Doe” |
In SQL (see the relational schema) and in OQL
this query can be formulated as:
|
SQL: OQL: |
select * from Emp where name = “Doe” |
The SBQL query returns a bag of references to
the Emp objects storing the
information about employees named Doe.
The references can be consumed by another SBQL operator or by a programming
statement based on SBQL, for instance, by the dot operator or by update statement. If the bag consists of
exactly one element, it can be automatically coerced (by another operator) to
this element. The SQL query returns a table with rows containing the values of Doe tuples. The OQL query returns a bag
with Doe objects.
|
E.AS0.4 |
Get the salary of “Doe”. |
|
SBQL: |
(Emp where name = “Doe”).sal |
In SQL (see the relational schema) and in OQL
this query can be formulated as:
|
SQL: OQL: |
select sal from Emp where name = “Doe” |
The SBQL query returns a bag of references to
the sal objects that are within Emp object storing the information about
employees named Doe. The SQL query returns one-column table with sal values of Doe tuples. The OQL query returns a bag of literals - values of sal of Doe objects. Due to the fact that SBQL returns references to sal objects rather than their values (as
SQL and OQL do) the above query can be used in an updating statement (set
Doe’s salary to 5000):
|
SBQL: |
(Emp where name = “Doe”).sal
:= 5000; |
This is equivalent to the SQL updating
statement:
|
SQL: |
update Emp set sal = 5000 where name = “Doe” |
However, SQL makes no explicit relationships of
this statement with the above SQL query, what is violation of the
orthogonality principle, resulting in many further disadvantages, in
particular implementation effort overhead, lower potential for query optimization,
longer user documentation, etc. Note also small (but painful) syntactic fault:
the operator = in one SQL statement is used in two meanings (assignment and
comparison) what could be an unexpected difficulty for a lot of beginners.
In the following we return to the topic of
nesting SBQL queries into imperative statements.
|
E.AS0.5 |
Get e#, name and job of employees earning
more than 1000. |
|
SBQL: |
(Emp where sal = 1000).struct(e#, name, job) |
|
SBQL: |
(Emp where sal = 1000).(e#, name, job) |
In SQL (see the relational schema) and in OQL
this query can be formulated as:
|
SQL: OQL: |
select e#, name, job from Emp where sal = 1000 |
The SBQL query returns a bag of structures struct{ie#, iname, ijob} with references to
proper subobjects stored within a corresponding Emp object. Note that the elements of the structure are unnamed
(although names can be derived from names of corresponding objects). In general
(in contrast to C/C++ and many other languages), for conceptual closure SBQL
takes the point of view that structures may contain unnamed elements.
References returned by the query are more universal than values returned by SQL
and OQL queries, because references can be automatically dereferenced by some
operators (as will be shown later), but also can be used within updating
statements. This is impossible in SQL and OQL.
|
E.AS0.6 |
Get worksIn
references for employees named Doe. |
|
SBQL: |
(Emp where name = “Doe”).worksIn |
For SQL this query is irrelevant. In OQL such a
query cannot be expressed. Such a query however makes a sense because it can be
a part of more complex queries (see the next one). Such a query makes also a
sense because a worksIn pointer needs
to be updated. This updating feature is commonly neglected by many proponents
of query languages. For instance, to move Doe to the Sales department it is necessary to assign to this pointer the
proper reference. In the imperative extension of SBQL such a request can take
the form of the following statement:
|
SBQL: |
(Emp where name = “Doe”).worksIn
:= ref(Dept where dname = “Sales”) |
On the left side of this statement we receive a
reference to a worksIn pointer. On the
right side of the statement we obtain a reference to the Sales department, which has to be assigned to the pointer. Such an
assignment can automatically trigger an action on the twin employs pointer, as in the C++ and Java bindings of the ODMG
standards.
|
E.AS0.7 |
Get all information on departments for
employees named Doe. |
|
SBQL: |
((Emp where (name = “Doe”)).worksIn).Dept |
|
SBQL: |
(Emp where name = “Doe”).worksIn.Dept |
The query returns a bag of references to Dept objects for persons named Doe. In
SQL (see the relational schema) this query can be formulated as follows (one of
several possible ways):
|
SQL: |
select * from Dept where d# in select d# from Emp where name = “Doe” |
In OQL this query can be formulated as (one of
several possible ways):
|
OQL: |
select d from Emp as e, e.worksIn
as d where e.name
= “Doe” |
Note that the OQL query avoids using the name Dept, which seems to be an advantage: queries
are shorter. We consider such a feature as disadvantageous for two reasons:
(1) For updating there is necessity to
distinguish a reference to worksIn
pointer and a reference to a Dept
objects. OQL makes such a distinction impossible, thus it would be difficult if
one would like to smoothly extend OQL with updating statements.
(2) SBQL queries are more legible, because they
explicitly show where the navigation is finished, i.e. on Dept objects in this case. In OQL the programmer sees only worksIn relationship thus must verify
the schema (which could be very large) to be sure what will be the result of
the navigation. Hence, the SBQL convention is better from the conceptual
modeling point of view.
|
E.AS0.8 |
Get names of departments for employees named
Doe. |
|
SBQL: |
(((Emp where (name = “Doe”)).worksIn).Dept).dname |
|
SBQL: |
(Emp where name = “Doe”).worksIn.Dept.dname |
The query returns a bag of references to dname objects for persons named Doe. In
SQL (see the relational schema) this query can be formulated as follows (one of
several possible ways):
|
SQL: |
select dname from Dept where d# in select d# from Emp where name = “Doe” |
In OQL this query can be formulated as (one of
several possible ways):
|
OQL: |
select e.worksIn.dname from Emp as e where
e.name = “Doe” |
The example illustrates so-called path expressions, that is, smooth
navigation along some path in the database schema graph. In SBA and SBQL path
expressions are side-effects of the definition of the binary dot operator (see
the first query) and the syntactic convention assuming the evaluation from left
to right (see the second SBQL query). Path expressions can be as long as
necessary, for instance (get the name of the Doe’s boss):
|
SBQL: |
(Emp where name = “Doe”).worksIn.Dept.boss.Emp.name |
Because we do not define path expressions as
special syntactic and semantic constructs (as for instance in [Kim89, Kife92,
Cri96]) we receive full freedom in combining path expressions with other SBQL
operators. We claim therefore that our path expressions have the highest
universality from all the proposals that can be found in the literature and in
other proposals of query languages. For instance, the following SBQL query
specifies names and cities for employees working in the department managed by
Kim:
|
SBQL: |
(Dept where (boss.Emp.name) = “Kim”).employs.Emp. (name,
if exists(Address) then Address.city else
“No address”) |
Other proposals concerning path expressions are
usually informal concerning their semantics, or the semantic specification is
limited. In particular, OQL allows for path expressions, but only in cases when
a sub-query before a dot returns a single element rather than a collection. In
other cases OQL requires to use the select...from...where
syntax, as in the above OQL query. Such limitations and requirements are of
course rejected during the development of SBQL.
|
E.AS0.9 |
Get all information on employees earning more
than Doe. |
|
SBQL: |
Emp where sal > ((Emp where name = “Doe”).sal) |
The query illustrates the possibility to nest
queries. It contains name sal two
times, but due to the stack-based semantics each of them is bound differently:
first sal is bound on ENVS having two
sections, and the second sal is bound
on ENVS having three sections. In SQL (see the relational schema) and OQL this
query can be formulated as follows (one of several possible ways):
|
SQL: OQL: |
select * from Emp where sal > select sal from Emp where name = “Doe” |
In our opinion, such SQL/OQL queries will also
require some environment stack concept, but it is not explicit in the
description of the languages’ semantics.
|
E.AS0.10 |
For each employee return all information about
the employee and his/her department. |
|
SBQL: |
Emp join (worksIn.Dept) |
The query uses the dependent join operator. The
result is a bag of structures struct{iEmp, iDept}, where iEmp
is a reference to an Emp object and iDept is a reference to the Dept object associated with the Emp object by the worksIn pointer. The corresponding OQL query is the following:
|
OQL: |
select struct(employee:e, department:d) from Emp as e, e.worksIn as d |
This is not a strict equivalent because in OQL elements
of structures must have tags (as employee
and department in the above query).
Besides that it is not quite clear what such a OQL query will return -
semantics of OQL is rather obscure (OQL does not introduce the concept of a
reference) and this operator is improperly described as “cartesian
product” (no comment). In SBQL such a query can also be issued to the
relational structure:
|
SBQL: |
Emp join (Dept where worksIn = d#) |
|
SBQL: |
struct(Emp, Dept) where worksIn = d# |
|
SBQL: |
(Emp,
Dept) where worksIn = d# |
The last query has an equivalent in SQL:
|
SQL: |
select * from Emp, Dept where worksIn = d#) |
The equivalence is not strict, because the SQL
query returns a table of values rather than references. In particular, such SBQL
query can be used in updating statements, while the SQL query cannot.
|
E.AS0.11 |
For each department get all information
together with the average salary of their employees. |
|
SBQL: |
Dept join avg(employs.Emp.sal) |
The result is a bag of structures struct{iDept, average_salary},
where iDept is a reference
to a Dept object and average_salary is the real number being
the average salary within this department. Changes of ENVS states during
evaluation of this query are presented in Fig.31. An equivalent OQL query
requires the use of the group by
operator, but for obscure semantics of this feature we do not risk to express
it. A similar SQL query also requires the use of the group by operator:
|
SQL: |
select d.*, avg(e.sal) from Dept as d, Emp as e where d.d# = e.worksIn group by d.d# |
The disadvantage of this query is the necessity
to use the group by operator, which is non-orthogonal to other operators,
implies far context semantic dependencies (is not compositional), is difficult
to optimize, causes some semantic reefs and sometimes requires an additional having clause. Note that the SBQL query
has 10 lexical tokens, while the equivalent SQL query - 31 tokens. We comment
this difference in the next section.
We also show that SBQL query can also address
the relational schema, with no necessity to use the group by option:
|
SBQL: |
Dept join avg((Emp where worksIn = d#).sal) |
|
SBQL: |
(Dept
as d) join avg(((Emp as e) where e.worksIn = d.d#).e.sal) |

Fig.31. A view on states of ENVS during the
progress in the evaluation of the query Dept
join avg(employs.Emp.sal)
This PowerPoint presentation shows all the steps that are
necessary to evaluate this query for a tiny database.
|
E.AS0.12 |
Get the average number of employees in all
the departments. |
|
SBQL: |
avg(Dept.count(employs)) |
For the relational schema:
|
SBQL: |
avg(Dept.count(Emp where d# = worksIn)) |
In the SQL-89 standard this query can be
expressed through an additional view. In SQL-92 this query can be expressed by
the group by clause. However, it
leads to a well-known semantic reef, which concerns e.g. the case when some
department has no employees. Such a department is not taken into account, hence
the average will be different from expected. In SBQL such a semantic reef does
not occur.
|
E.AS0.13 |
For each employee that earns more than 2000
and works in a department located in Paris get name, job, department name and
the name of his/her boss. |
|
SBQL: |
((Emp join (works_in.Dept)) where sal > 2000 and
“ |
In the result we obtain a bag of structures struct{iempname, iempjob,
idname, ibossname}, where references iempname, iempjob, idname, ibossname
refer to particular attributes. A similar SQL query is about two times longer:
|
SQL: |
select e.name,
e.job, d.dname, b.name from Emp
as e, Dept as d, Emp as b, Location as l where e.sal > 2000 and l.loc = “ and e.worksIn = d.d# and d.boss = b.e# and l.d# = d.d# |
|
E.AS0.14 |
Get departments employing a professional for
any job in the company. |
|
SBQL: |
Dept where " distinct(Emp.job) as j ($employs.Emp (j = job)) |
A similar SQL query is very hard to express.
|
E.AS0.15 |
Is it true that each department employs an
employee earning more than his/her boss?. |
|
SBQL: |
"Dept ($employs.Emp (sal > boss.Emp.sal)) |
|
SBQL: |
"Dept as d ($d.employs.Emp as e (e.sal > d.boss.Emp.sal)) |
Such a query cannot be expressed in SQL,
because SQL does not introduce queries returning boolean values. Moreover,
because SQL has no explicit quantifiers, a similar query (e.g. returning 1 for true and 0 for false) requires the use of exists
or count functions, what leads to an
extremely clumsy statement. In OQL the query can be expressed as
follows:
|
OQL: |
for all d in
Dept : exists e in
select x from d.worksIn as x : e.sal > select
y.sal from d.boss as y |
The reader may now evaluate if the select...from... sugar and the
obligatory use of auxiliary iteration variables are useful or annoying
features. Note that the first SBQL query has 17 lexical elements, while the OQL
query - 31. Which of the queries is easier to write and comprehend?
|
E.AS0.16 |
Get names and salaries of designers who earn
more than their bosses. |
We present several styles that can be applied
to formulate of this SBQL query. First we present the SQL style (see the
relational schema):
|
SBQL, SQL style: |
((Emp as e, Dept as
d, Emp as b) where e.job = “designer” and e.worksIn = d.d# and d.boss = b.e# and e.sal > b.sal) . (e.name, e.sal) |
A corresponding SQL query is very similar;
basically there are only minor syntactic differences:
|
SQL: |
select e.name,
e.sal from Emp as
e, Dept as d, Emp as b where e.job = “designer” and e.worksIn = d.d# and d.boss = b.e# and e.sal > b.sal |
We can also present this query in the OQL
style:
|
SBQL, OQL style: |
(((Emp as e join e.worksIn.Dept as d ) join d.boss.Emp as b) where e.job =
“designer” and e.sal > b.sal) . (e.name, e.sal) |
A corresponding OQL query has also only minor
syntactic differences:
|
OQL: |
select e.name,
e.sal from Emp as
e, e.worksIn.Dept as d ,
d.boss.Emp as b where e.job = “designer”
and e.sal > b.sal |
SBQL allows for more compact form of this
query:
|
SBQL: |
(Emp where job =
“designer” and sal > (worksIn.Dept.boss.Emp.sal)). (name,
sal) |
SBQL allows also for the “domain
calculus” style of this query (see the relational schema). This style is
sometimes claimed to be more friendly and better to optimize (we do not believe
in that). Sometimes OQL is claimed to be based on this style (although this is
contradictory to another claim that OQL is similar to SQL, which is based on
the “tuple calculus” style) . The style is also the property of QBE
and RDQL.
|
SBQL, domain
calculus style: |
((((Emp.(name as en, sal as es, job as
ej, workdIn as ed)), (Dept.(d# as dd, boss as db)), (Emp.(sal as bs, e# as be))) where ej
= “designer” and ed = dd and db = be and es > bs). (en, es) |
By these examples we would like to show that
SBQL allows for many styles of querying on equal rights. The old debates on
advantage of one style over another one are totally irrelevant on the ground of
SBA and SBQL. Any inferences concerning ability of particular database models
to support query languages are attempts to make false stereotypes. Contrary to
the Michael
Stonebraker’s thesis, all database models have equal rights
concerning query languages. There is no advantage of a particular model,
including the object-relational model.
In the next section we will present just
otherwise thesis: why the object-relational model we consider disadvantageous
w.r.t. query languages.
Last modified: December 31, 2007