@@ -20,14 +20,15 @@ TutorialD is strongly-typed. The basic built-in types are:
21|Type Name|Explanation|Example|
21|Type Name|Explanation|Example|
22|---------|-----------|-------|
22|---------|-----------|-------|
23−|text|arbitrary text|"The Old Man and the Sea"|
23+|Text|arbitrary text|"The Old Man and the Sea"|
24−|int|any integer|-4|
24+|Integer|arbitarily-sized integer|-4|
25+|Int|machine word integer|int(10)|
25−|datetime|timestamp UTC|dateTimeFromEpochSeconds(1502304846)|
26+|DateTime|timestamp UTC|dateTimeFromEpochSeconds(1502304846)|
26−|date|calendar date|fromGregorian(2017,05,30)|
27+|Date|calendar date|fromGregorian(2017,05,30)|
27−|double|floating point number|3.1459|
28+|Double|floating point number|3.1459|
28−|bool|boolean value|t|
29+|Bool|boolean value|t|
29−|bytestring|arbitrary-length string of bytes- input is base64-encoded|bytestring("dGVzdGRhdGE=")|
30+|Bytestring|arbitrary-length string of bytes- input is base64-encoded|bytestring("dGVzdGRhdGE=")|
30−|interval x|interval/range type for ints, doubles, datetimes, and dates|interval(3,5,f,f)|
31+|Interval x|interval/range type for ints, doubles, datetimes, and dates|interval(3,5,f,f)|
32With regards to boolean values, be sure not to conflate ```t``` or ```f``` as a boolean value with ```true``` and ```false``` which are relation variables.
33With regards to boolean values, be sure not to conflate ```t``` or ```f``` as a boolean value with ```true``` and ```false``` which are relation variables.
@@ -48,7 +49,7 @@ Relation Variables are named cells which reference relations within a transactio
49Definition of a relation variable:
50Definition of a relation variable:
51−products :: {name char, age int}
52+products :: {name Text, age Integer}
53This expression creates a new relation variable in the current context which can refer to any relation with the header of "name" of type "char" and "age" of type int.
54This expression creates a new relation variable in the current context which can refer to any relation with the header of "name" of type "char" and "age" of type int.
@@ -151,15 +152,15 @@ The unary rename operator outputs a new relation with chosen attributes renamed.
153TutorialD (master/main): :showexpr s rename {city as town}
154TutorialD (master/main): :showexpr s rename {city as town}
154−┌──┬─────┬──────┬──────┐
155+┌────────┬───────────┬───────────────┬──────────┐
155−│s#│sname│status│town │
156+│s#::Text│sname::Text│status::Integer│town::Text│
156−├──┼─────┼──────┼──────┤
157+├────────┼───────────┼───────────────┼──────────┤
157−│S3│Blake│30 │Paris │
158+│"S3" │"Blake" │30 │"Paris" │
158−│S4│Clark│20 │London│
159+│"S4" │"Clark" │20 │"London" │
159−│S5│Adams│30 │Athens│
160+│"S5" │"Adams" │30 │"Athens" │
160−│S1│Smith│20 │London│
161+│"S1" │"Smith" │20 │"London" │
161−│S2│Jones│10 │Paris │
162+│"S2" │"Jones" │10 │"Paris" │
162−└──┴─────┴──────┴──────┘
163+└────────┴───────────┴───────────────┴──────────┘
165#### Projection
166#### Projection
@@ -170,14 +171,15 @@ For example:
172TutorialD (master/main): :showexpr p{color,city}
173TutorialD (master/main): :showexpr p{color,city}
173−┌──────┬─────┐
174+┌──────────┬───────────┐
174−│city │color│
175+│city::Text│color::Text│
175−├──────┼─────┤
176+├──────────┼───────────┤
176−│London│Red │
177+│"London" │"Red" │
177−│Paris │Blue │
178+│"Paris" │"Green" │
178−│Oslo │Blue │
179+│"Oslo" │"Blue" │
179−│Paris │Green│
180+│"Paris" │"Blue" │
180−└──────┴─────┘
181+└──────────┴───────────┘
183Projection attributes can also be inverted using `all but`:
185Projection attributes can also be inverted using `all but`:
@@ -217,22 +219,22 @@ Restriction is applied using a "where" clause, much like in SQL.
219TutorialD (master/main): :showexpr p where color="Blue" and city="Paris"
221TutorialD (master/main): :showexpr p where color="Blue" and city="Paris"
220−┌─────┬─────┬──┬─────┬──────┐
222+┌──────────┬───────────┬────────┬───────────┬───────────────┐
221−│city │color│p#│pname│weight│
223+│city::Text│color::Text│p#::Text│pname::Text│weight::Integer│
222−├─────┼─────┼──┼─────┼──────┤
224+├──────────┼───────────┼────────┼───────────┼───────────────┤
223−│Paris│Blue │P5│Cam │12 │
225+│"Paris" │"Blue" │"P5" │"Cam" │12 │
224−└─────┴─────┴──┴─────┴──────┘
226+└──────────┴───────────┴────────┴───────────┴───────────────┘
227The restriction predicate can be built from "and", "not", and "or". Boolean atom functions can also appear in a restriction as long as they are preceded by "^". For example:
229The restriction predicate can be built from "and", "not", and "or". Boolean atom functions can also appear in a restriction as long as they are preceded by "^". For example:
230TutorialD (master/main): :showexpr s where ^lt(@status,20)
232TutorialD (master/main): :showexpr s where ^lt(@status,20)
231−┌─────┬──┬─────┬──────┐
233+┌──────────┬────────┬───────────┬───────────────┐
232−│city │s#│sname│status│
234+│city::Text│s#::Text│sname::Text│status::Integer│
233−├─────┼──┼─────┼──────┤
235+├──────────┼────────┼───────────┼───────────────┤
234−│Paris│S2│Jones│10 │
236+│"Paris" │"S2" │"Jones" │10 │
235−└─────┴──┴─────┴──────┘
237+└──────────┴────────┴───────────┴───────────────┘
@@ -241,21 +243,22 @@ Joins are binary operators and are applied with the "join" keyword. The equivale
243TutorialD (master/main): :showexpr s join sp
245TutorialD (master/main): :showexpr s join sp
244−┌──────┬──┬───┬──┬─────┬──────┐
246+┌──────────┬────────┬────────────┬────────┬───────────┬───────────────┐
245−│city │p#│QTY│s#│sname│status│
247+│city::Text│p#::Text│qty::Integer│s#::Text│sname::Text│status::Integer│
246−├──────┼──┼───┼──┼─────┼──────┤
248+├──────────┼────────┼────────────┼────────┼───────────┼───────────────┤
249+│"London" │"P2" │200 │"S1" │"Smith" │20 │
247−│London│P6│100│S1│Smith│20 │
250+│"London" │"P6" │100 │"S1" │"Smith" │20 │
248−│London│P3│400│S1│Smith│20 │
251+│"London" │"P3" │400 │"S1" │"Smith" │20 │
252+│"Paris" │"P2" │400 │"S2" │"Jones" │10 │
249−│London│P5│400│S4│Clark│20 │
253+│"London" │"P5" │400 │"S4" │"Clark" │20 │
254+│"Paris" │"P1" │300 │"S2" │"Jones" │10 │
250−│London│P1│300│S1│Smith│20 │
255+│"London" │"P1" │300 │"S1" │"Smith" │20 │
251−│Paris │P2│200│S3│Blake│30 │
256+│"Paris" │"P2" │200 │"S3" │"Blake" │30 │
252−│Paris │P1│300│S2│Jones│10 │
257+│"London" │"P4" │300 │"S4" │"Clark" │20 │
253−│London│P5│100│S1│Smith│20 │
254−│London│P4│300│S4│Clark│20 │
258+│"London" │"P2" │200 │"S4" │"Clark" │20 │
255−│Paris │P2│400│S2│Jones│10 │
256−│London│P2│200│S1│Smith│20 │
259+│"London" │"P4" │200 │"S1" │"Smith" │20 │
257−│London│P4│200│S1│Smith│20 │
260+│"London" │"P5" │100 │"S1" │"Smith" │20 │
258−└──────┴──┴───┴──┴─────┴──────┘
261+└──────────┴────────┴────────────┴────────┴───────────┴───────────────┘
261As a convenience, `tutd` also supports semijoin (`matching`) and antijoin (`not matching`) syntax.
264As a convenience, `tutd` also supports semijoin (`matching`) and antijoin (`not matching`) syntax.
@@ -269,16 +272,16 @@ As a convenience, `tutd` also supports semijoin (`matching`) and antijoin (`not
269The binary union operator merges tuples from both relations if-and-only-if the attributes of both relations are identical. The SQL equivalent is the "UNION" operator. Unlike SQL, however, duplicate tuples are not tolerated, so there is no "UNION ALL" equivalent in the relational algebra.
272The binary union operator merges tuples from both relations if-and-only-if the attributes of both relations are identical. The SQL equivalent is the "UNION" operator. Unlike SQL, however, duplicate tuples are not tolerated, so there is no "UNION ALL" equivalent in the relational algebra.
272−TutorialD (master/main): :showexpr S union S
275+TutorialD (master/main): :showexpr s union s
273−┌──────┬──┬─────┬──────┐
276+┌──────────┬────────┬───────────┬───────────────┐
274−│city │s#│sname│status│
277+│city::Text│s#::Text│sname::Text│status::Integer│
275−├──────┼──┼─────┼──────┤
278+├──────────┼────────┼───────────┼───────────────┤
276−│Paris │S3│Blake│30 │
279+│"Paris" │"S3" │"Blake" │30 │
277−│London│S4│Clark│20 │
278−│Athens│S5│Adams│30 │
280+│"Athens" │"S5" │"Adams" │30 │
279−│London│S1│Smith│20 │
281+│"London" │"S1" │"Smith" │20 │
280−│Paris │S2│Jones│10 │
282+│"Paris" │"S2" │"Jones" │10 │
283+│"London" │"S4" │"Clark" │20 │
281−└──────┴──┴─────┴──────┘
284+└──────────┴────────┴───────────┴───────────────┘
284The union of any relation with itself is itself.
287The union of any relation with itself is itself.
@@ -289,15 +292,15 @@ The extend unary operator adds an attribute to a relation's header and body and
291TutorialD (master/main): :showexpr s:{status2:=add(10,@status)}
294TutorialD (master/main): :showexpr s:{status2:=add(10,@status)}
292−┌──────┬──┬─────┬──────┬───────┐
295+┌──────────┬────────┬───────────┬───────────────┬────────────────┐
293−│city │s#│sname│status│status2│
296+│city::Text│s#::Text│sname::Text│status::Integer│status2::Integer│
294−├──────┼──┼─────┼──────┼───────┤
297+├──────────┼────────┼───────────┼───────────────┼────────────────┤
295−│Paris │S2│Jones│10 │20 │
298+│"Paris" │"S2" │"Jones" │10 │20 │
296−│Athens│S5│Adams│30 │40 │
299+│"Athens" │"S5" │"Adams" │30 │40 │
300+│"London" │"S4" │"Clark" │20 │30 │
297−│Paris │S3│Blake│30 │40 │
301+│"Paris" │"S3" │"Blake" │30 │40 │
298−│London│S1│Smith│20 │30 │
302+│"London" │"S1" │"Smith" │20 │30 │
299−│London│S4│Clark│20 │30 │
300−└──────┴──┴─────┴──────┴───────┘
303+└──────────┴────────┴───────────┴───────────────┴────────────────┘
303The "@" is required in the above query in order to distinguish the attribute's name from a relation's name.
306The "@" is required in the above query in order to distinguish the attribute's name from a relation's name.
@@ -323,27 +326,27 @@ The group operator can also be used to emulate functionality similar to SQL's OU
325TutorialD (master/main): :showexpr s group ({sname,status,s#} as subrel)
328TutorialD (master/main): :showexpr s group ({sname,status,s#} as subrel)
326−┌──────┬─────────────────┐
329+┌──────────┬───────────────────────────────────────────────────────┐
327−│city │subrel │
330+│city::Text│subrel::relation {s#::Text,sname::Text,status::Integer}│
328−├──────┼─────────────────┤
331+├──────────┼───────────────────────────────────────────────────────┤
329−│Athens│┌──┬─────┬──────┐│
332+│"Athens" │┌────────┬───────────┬───────────────┐ │
330−│ ││s#│sname│status││
333+│ ││s#::Text│sname::Text│status::Integer│ │
331−│ │├──┼─────┼──────┤│
334+│ │├────────┼───────────┼───────────────┤ │
332−│ ││S5│Adams│30 ││
335+│ ││"S5" │"Adams" │30 │ │
333−│ │└──┴─────┴──────┘│
334−│London│┌──┬─────┬──────┐│
335−│ ││s#│sname│status││
336−│ │├──┼─────┼──────┤│
339−│ │└──┴─────┴──────┘│
336+│ │└────────┴───────────┴───────────────┘ │
340−│Paris │┌──┬─────┬──────┐│
337+│"Paris" │┌────────┬───────────┬───────────────┐ │
341−│ ││s#│sname│status││
338+│ ││s#::Text│sname::Text│status::Integer│ │
342−│ │├──┼─────┼──────┤│
339+│ │├────────┼───────────┼───────────────┤ │
344−│ ││S2│Jones│10 ││
340+│ ││"S2" │"Jones" │10 │ │
341+│ ││"S3" │"Blake" │30 │ │
342+│ │└────────┴───────────┴───────────────┘ │
343+│"London" │┌────────┬───────────┬───────────────┐ │
344+│ ││s#::Text│sname::Text│status::Integer│ │
345+│ │├────────┼───────────┼───────────────┤ │
346+│ ││"S4" │"Clark" │20 │ │
347+│ ││"S1" │"Smith" │20 │ │
345−│ │└──┴─────┴──────┘│
348+│ │└────────┴───────────┴───────────────┘ │
346−└──────┴─────────────────┘
349+└──────────┴───────────────────────────────────────────────────────┘
349#### Ungroup
352#### Ungroup
@@ -352,15 +355,15 @@ The unary ungroup operator "unwraps" subrelations in a relation-valued attribute
354TutorialD (master/main): :showexpr (s group ({sname,status,s#} as subrel)) ungroup subrel
357TutorialD (master/main): :showexpr (s group ({sname,status,s#} as subrel)) ungroup subrel
355−┌──────┬──┬─────┬──────┐
358+┌──────────┬────────┬───────────┬───────────────┐
356−│city │s#│sname│status│
359+│city::Text│s#::Text│sname::Text│status::Integer│
357−├──────┼──┼─────┼──────┤
360+├──────────┼────────┼───────────┼───────────────┤
361+│"Paris" │"S2" │"Jones" │10 │
358−│Paris │S3│Blake│30 │
362+│"Paris" │"S3" │"Blake" │30 │
359−│London│S4│Clark│20 │
363+│"London" │"S4" │"Clark" │20 │
364+│"London" │"S1" │"Smith" │20 │
360−│Athens│S5│Adams│30 │
365+│"Athens" │"S5" │"Adams" │30 │
361−│London│S1│Smith│20 │
362−│Paris │S2│Jones│10 │
363−└──────┴──┴─────┴──────┘
366+└──────────┴────────┴───────────┴───────────────┘
366#### Minus
369#### Minus
@@ -393,11 +396,11 @@ The relation variable assignment mentioned above certainly changes the state of
394TutorialD (master/main): newrelvar:=relation{tuple{age 3}}
397TutorialD (master/main): newrelvar:=relation{tuple{age 3}}
395TutorialD (master/main): :showexpr newrelvar
398TutorialD (master/main): :showexpr newrelvar
396−┌───┐
399+┌────────────┐
397−│age│
400+│age::Integer│
398−├───┤
401+├────────────┤
400−└───┘
403+└────────────┘
403All further operators are convenience operators and could be implemented with simple assignment. SQL has no equivalent to relational assignment; instead, one must issue "CREATE TABLE" commands and insert rows into the resultant tables.
406All further operators are convenience operators and could be implemented with simple assignment. SQL has no equivalent to relational assignment; instead, one must issue "CREATE TABLE" commands and insert rows into the resultant tables.
@@ -419,16 +422,16 @@ The insert operators accepts a relation variable and a relation of the same type
420TutorialD (master/main): insert s relation{tuple{city "Boston",s# "S10",sname "Gonzalez",status 10}}
423TutorialD (master/main): insert s relation{tuple{city "Boston",s# "S10",sname "Gonzalez",status 10}}
421TutorialD (master/main): :showexpr s
424TutorialD (master/main): :showexpr s
422−┌──────┬───┬────────┬──────┐
425+┌──────────┬────────┬───────────┬───────────────┐
423−│city │s# │sname │status│
426+│city::Text│s#::Text│sname::Text│status::Integer│
424−├──────┼───┼────────┼──────┤
427+├──────────┼────────┼───────────┼───────────────┤
425−│Paris │S3 │Blake │30 │
426−│London│S4 │Clark │20 │
427−│Athens│S5 │Adams │30 │
428+│"Athens" │"S5" │"Adams" │30 │
428−│London│S1 │Smith │20 │
429−│Paris │S2 │Jones │10 │
429+│"Paris" │"S2" │"Jones" │10 │
430−│Boston│S10│Gonzalez│10 │
430+│"Boston" │"S10" │"Gonzalez" │10 │
431+│"London" │"S4" │"Clark" │20 │
432+│"London" │"S1" │"Smith" │20 │
433+│"Paris" │"S3" │"Blake" │30 │
431−└──────┴───┴────────┴──────┘
434+└──────────┴────────┴───────────┴───────────────┘
434This insertion operation is equivalent to:
437This insertion operation is equivalent to:
@@ -443,15 +446,15 @@ The update operator accepts a relation argument and a predicate, optionally filt
444TutorialD (master/main): update s where status=20 (sname:="Mr. Twenty")
447TutorialD (master/main): update s where status=20 (sname:="Mr. Twenty")
445TutorialD (master/main): :showexpr s
448TutorialD (master/main): :showexpr s
446−┌──────┬──┬──────────┬──────┐
449+┌──────────┬────────┬────────────┬───────────────┐
447−│city │s#│sname │status│
450+│city::Text│s#::Text│sname::Text │status::Integer│
448−├──────┼──┼──────────┼──────┤
451+├──────────┼────────┼────────────┼───────────────┤
452+│"Paris" │"S2" │"Jones" │10 │
453+│"London" │"S1" │"Mr. Twenty"│20 │
449−│Paris │S3│Blake │30 │
454+│"Paris" │"S3" │"Blake" │30 │
450−│London│S1│Mr. Twenty│20 │
455+│"London" │"S4" │"Mr. Twenty"│20 │
451−│Athens│S5│Adams │30 │
456+│"Athens" │"S5" │"Adams" │30 │
452−│Paris │S2│Jones │10 │
453−│London│S4│Mr. Twenty│20 │
454−└──────┴──┴──────────┴──────┘
457+└──────────┴────────┴────────────┴───────────────┘
457This is logically equivalent to:
460This is logically equivalent to:
@@ -459,15 +462,15 @@ This is logically equivalent to:
460TutorialD (master/main): s:=(((s where status=20){city,s#,status}):{sname:="Mr. Twenty"}) union (s where not status=20)
463TutorialD (master/main): s:=(((s where status=20){city,s#,status}):{sname:="Mr. Twenty"}) union (s where not status=20)
461TutorialD (master/main): :showexpr s
464TutorialD (master/main): :showexpr s
462−┌──────┬──┬──────────┬──────┐
465+┌──────────┬────────┬────────────┬───────────────┐
463−│city │s#│sname │status│
466+│city::Text│s#::Text│sname::Text │status::Integer│
464−├──────┼──┼──────────┼──────┤
467+├──────────┼────────┼────────────┼───────────────┤
468+│"Paris" │"S2" │"Jones" │10 │
469+│"London" │"S1" │"Mr. Twenty"│20 │
465−│Paris │S3│Blake │30 │
470+│"Paris" │"S3" │"Blake" │30 │
466−│London│S1│Mr. Twenty│20 │
471+│"London" │"S4" │"Mr. Twenty"│20 │
467−│Athens│S5│Adams │30 │
472+│"Athens" │"S5" │"Adams" │30 │
468−│Paris │S2│Jones │10 │
469−│London│S4│Mr. Twenty│20 │
470−└──────┴──┴──────────┴──────┘
473+└──────────┴────────┴────────────┴───────────────┘
473This query means: "Drop the sname attribute from S filtered where status equals 20, extend the resultant relation by sname with value 'Mr. Twenty', then union the resultant relation with S filtered by status not equaling 20, and assign that to S."
476This query means: "Drop the sname attribute from S filtered where status equals 20, extend the resultant relation by sname with value 'Mr. Twenty', then union the resultant relation with S filtered by status not equaling 20, and assign that to S."
@@ -479,19 +482,19 @@ The delete operator accepts a relation argument and a filtering predicate, creat
480TutorialD (master/main): delete sp where s#="S4"
483TutorialD (master/main): delete sp where s#="S4"
481TutorialD (master/main): :showexpr sp
484TutorialD (master/main): :showexpr sp
482−┌──┬───┬──┐
485+┌────────┬────────────┬────────┐
483−│p#│QTY│s#│
486+│p#::Text│qty::Integer│s#::Text│
484−├──┼───┼──┤
487+├────────┼────────────┼────────┤
491−│P2│200│S3│
488+│"P2" │200 │"S3" │
492−│P1│300│S2│
490+│"P1" │300 │"S1" │
493−│P5│100│S1│
491+│"P5" │100 │"S1" │
494−└──┴───┴──┘
497+└────────┴────────────┴────────┘
497This is logically equivalent to:
500This is logically equivalent to:
@@ -526,7 +529,7 @@ Foreign keys are constraints which require that certain values appearing in one
526TutorialD (master/main): foreign key s#_in_sp sp{s#} in s{s#}
529TutorialD (master/main): foreign key s#_in_sp sp{s#} in s{s#}
529−This expression ensure that any sp{s#} must also appear as a value in s{s#}. Note that the sub-expression (on the left of "in") and the super-expression (on the right) must be of the same relation type- the generated relation values' attribute names and types must be equal. Use `rename` to make attribute names identical.
532+This expression ensure that any sp{s#} must also appear as a value in s{s#}. Note that the sub-expression (on the left of "in") and the super-expression (on the right) must be of the same relation type- the generated relation values' attribute names and types must be equal. Use `rename` to make attribute names identical.
531#### Functional dependencies
534#### Functional dependencies
@@ -560,27 +563,27 @@ SQL supports aggregate queries using aggregate functions (SUM(), COUNT(), AVERAG
562TutorialD (master/main): :showexpr s group ({s#,sname,status} as subrel):{citycount:=count(@subrel)}
565TutorialD (master/main): :showexpr s group ({s#,sname,status} as subrel):{citycount:=count(@subrel)}
563−┌──────┬─────────┬─────────────────┐
566+┌──────────┬──────────────────┬───────────────────────────────────────────────────────┐
564−│city │citycount│subrel │
567+│city::Text│citycount::Integer│subrel::relation {s#::Text,sname::Text,status::Integer}│
568+├──────────┼──────────────────┼───────────────────────────────────────────────────────┤
569+│"Athens" │1 │┌────────┬───────────┬───────────────┐ │
570+│ │ ││s#::Text│sname::Text│status::Integer│ │
565−├──────┼─────────┼─────────────────┤
571+│ │ │├────────┼───────────┼───────────────┤ │
572+│ │ ││"S5" │"Adams" │30 │ │
573+│ │ │└────────┴───────────┴───────────────┘ │
566−│Paris │2 │┌──┬─────┬──────┐│
574+│"Paris" │2 │┌────────┬───────────┬───────────────┐ │
567−│ │ ││s#│sname│status││
575+│ │ ││s#::Text│sname::Text│status::Integer│ │
568−│ │ │├──┼─────┼──────┤│
576+│ │ │├────────┼───────────┼───────────────┤ │
570−│ │ ││S2│Jones│10 ││
577+│ │ ││"S2" │"Jones" │10 │ │
578+│ │ ││"S3" │"Blake" │30 │ │
571−│ │ │└──┴─────┴──────┘│
579+│ │ │└────────┴───────────┴───────────────┘ │
572−│London│2 │┌──┬─────┬──────┐│
580+│"London" │2 │┌────────┬───────────┬───────────────┐ │
573−│ │ ││s#│sname│status││
581+│ │ ││s#::Text│sname::Text│status::Integer│ │
574−│ │ │├──┼─────┼──────┤│
582+│ │ │├────────┼───────────┼───────────────┤ │
583+│ │ ││"S4" │"Clark" │20 │ │
575−│ │ ││S1│Smith│20 ││
584+│ │ ││"S1" │"Smith" │20 │ │
577−│ │ │└──┴─────┴──────┘│
585+│ │ │└────────┴───────────┴───────────────┘ │
578−│Athens│1 │┌──┬─────┬──────┐│
579−│ │ ││s#│sname│status││
580−│ │ │├──┼─────┼──────┤│
582−│ │ │└──┴─────┴──────┘│
583−└──────┴─────────┴─────────────────┘
586+└──────────┴──────────────────┴───────────────────────────────────────────────────────┘
586In this query result, we can simultaneously answer how many suppliers are located in each city and which suppliers they are. The expression is constructed by first grouping to isolate the city, then extending the query to add a attribute containing the tuple count for each subrelation.
589In this query result, we can simultaneously answer how many suppliers are located in each city and which suppliers they are. The expression is constructed by first grouping to isolate the city, then extending the query to add a attribute containing the tuple count for each subrelation.