Fields and Joins

A typical database defines many data tables, and many fields (columns) within each table. Hiding and showing fields, within and across tables, is a frequent operation in Ultorg.

The Field Selector

To pick fields to display in a perspective, open the context menu on any already-visible field in the relevant subquery, and click Fields & Joins. This opens the field selector popup, where adjacent fields can be hidden and shown:

The popup version of the field selector, opened via the Fields & Joins action.

You can quickly open the field selector with Ctrl+K (⌘K on Mac), and use the Up/Down arrow keys and Space to select and check relevant fields. Holding down Shift while pressing arrow keys lets you select multiple fields at once (see Keyboard Shortcuts).

Multiple selection in the Fields popup.

You can type the first few characters of a field name to move the selection to said field.

Type to search in the field selector.

Checkbox Behavior

A checkbox (Checkbox widget) controls the visibility of each field within the current perspective. Checking and unchecking Select All reverts to a default set of visible fields.

It is possible to expand a subquery in the field selector without immediately making it visible. In this case you may see child fields with a dimly checked checkbox (Half-checked checkbox widget). These are fields that would become visible if their parent subquery is made visible.

Icons in the Field Selector

In the field selector, the following icons are used for different kinds of fields:

Subquery iconA subquery associated with a data table.
Custom Group iconA subquery not associated with a data table (a Custom Group).
Suggested join iconA suggested join that can be added as a new subquery.
Table column iconA primitive field pulling data from a column in the containing subquery's data table.
Calculated field iconA primitive field calculating values with a formula.
Compact Join iconA primitive field displayed together with a joined subquery in a compact join.

Less commonly:

New table column iconA new available table column, which only recently appeared in the data source.
Compact Join formula iconA compact join on a calculated field.

A field selector is also shown in the permanent Fields sidebar. It highlights the field where the cell cursor is currently located.

The Fields sidebar.

Showing Additional Properties

You can display additional information about each field by clicking the property selection button (Property selection button icon) in the corner. For example, you can show the data type of each table column:

Selecting additional properties for display in the Fields sidebar.

The sidebar version of the field selector is useful for quick glances at the field list. For changing field visibilities, the popup version in the Fields & Joins action is usually more effective, as it keeps your visual focus (eye gaze and mouse pointer) closer to the actual data.

Hide and Hide Other

The Hide and Hide Other actions in the context menu can be used to quickly hide selected or unselected fields, without having to go through the field selector.

The Hide Other action.

The Hide actions are often used with multiple selection, as illustrated above.

Semantics of Hidden Fields

In most cases, hiding or showing a field (primitive or subquery) has no effect on the semantics of a perspective query, beyond the change in visibility itself. The exceptions are:

  • In a Custom Group, grouping is done based on visible primitive fields only.
  • For data tables without a primary key or physical row ID, associated subqueries will retrieve unique rows only, based on visible primitive fields.
  • For subqueries with no visible primitive fields, and at most a single visible subquery, retrieved rows will be collapsed into one.
    A perspective query where the Sections and Instructor Assignments subqueries each have no visible primitive fields and only a single visible child subquery.

    (In the example above, individual rows in the Sections and Instructor Assignments tables are collapsed because no primitive fields are visible in the associated subqueries. The Instructors subquery can still retrieve multiple rows, as it has visible primitive fields.)

  • For the rules above, primitive fields that are sorted on are treated as if visible.

For subqueries with an associated data table and primary key, and at least one visible primitive field, each row displayed in Ultorg will correspond to a single row in the underlying data table. In the other cases detailed above, an Ultorg subquery behaves like a SELECT DISTINCT in SQL.

Semantic effects aside, hiding a subquery may speed up certain queries.

Joins between Tables

In database queries, a join is an operation that combines data from two tables, based on common identifiers such as email addresses, room numbers, or customer IDs. Ultorg perspectives are structured around joins and the relationships they expose between tables.

Many databases provide an explicit list of useful joins, known as foreign key relationships. When you invoke Fields & Joins to open the field selector, Ultorg will show not only the fields of the current table (Table column icon), but also suggested joins (Suggested join icon) based on known foreign key relationships. You can click the corresponding checkbox to make the join visible in your perspective:

Selecting a joined subquery in the Fields popup.

The example above is from the demo database, which you can access via Add Data Source→Connect to Demo Database. In this perspective, we show a list of university courses and, for each course, its reading list.

Joins and One-to-Many Relationships

By making a few more fields visible, we can see how joins work in Ultorg:

A perspective joining two tables, with correlated ID fields visible.

Here, the Course ID field in the Readings table is matched against the ID field in the Courses table. This exposes a one-to-many relationship: each course can have many readings. Most foreign key relationships are also one-to-many relationships.

The presence of a join condition is indicated by the join icon (Join icon) after the field name Course ID in the table header. The full join condition is shown in the field selector. If you don't need to see the ID fields, you can hide them; this will not affect the semantics of the database query.

Suggested Joins

The previous example showed a join that was automatically suggested based on a foreign key relationship. You can also specify joins manually, using the Custom Join action. After a join has been added manually once, it will be included in the field selector's subsequent suggestions.

Suggested joins appear in the field selector from either direction, i.e. with either table in the join as either the parent or the child subquery.

Primary Key Columns

A data table may designate a one or more columns as its primary key. Each row is uniquely identified by the value in the primary key column(s). For example, a “Customers” table might have a primary key column named “ID” that holds customer IDs.

In the Fields sidebar, primary key columns are indicated with the key icon (Primary Key icon).

A table's primary key column indicated with a key icon next to the applicable field in the Fields sidebar.

Certain Ultorg features require, or benefit from, the presence of a primary key. For example:

  • A subquery that joins on its primary key will always yield at most one row per parent row. This makes references to the subquery from a formula permissible in more cases, without the use of an aggregate function.
  • The Compact Join feature works specifically for joins against a single-column primary key.
  • Data editing is supported only on data tables that define a primary key.
  • When a subquery has no explicit sorting, Ultorg may sort on its table's primary key.
  • For tables with a primary key, Ultorg may generate more efficient SQL queries.

A foreign key relationship, as mentioned earlier, has foreign key column(s) in one table that reference another table's primary key.

If you need to change the set of primary key columns for a data table, you must do so at the data source level, e.g. with the ALTER TABLE command in SQL. For Excel and Google Sheets connections, the first column of each table is used as a primary key if its values are unique.

For tables without a primary key, a physical row ID may sometimes be available instead, such as ctid on PostgreSQL. In these cases, Ultorg will add the physical row ID to the field list, and use it as a primary key for grouping and row identification purposes.

Join Visualization in Ultorg

The joins in an Ultorg perspective work differently than the traditional “flat” joins encountered in SQL and other query tools. Rather than duplicating rows from one table for each matching row in the other, Ultorg displays parent rows and a set of matching rows under each.

Expanding the example, we can add more joins under the Courses table, by including additional suggested joins (Suggested join icon) from the field selector. In addition to the reading list for each course, we display Sections and, under the latter, Meetings:

A perspective showing three joins between four data tables, including two parallel one-to-many relationships (Courses to Readings and Courses to Sections), and a one-to-many relationships nested in another one-to-many relationship (Courses to Sections to Meetings).

Each Course is now displayed with both a Reading list and a Section list, and each Section list in turn has a list of Meetings. By default, a Course is displayed even if its Reading list or Section list is empty, as with the course “Neurobiology” above. The latter behavior can be adjusted with the Hide Parent If Empty (Hide Parent If Empty icon) setting.

This is a useful visualization that cannot be produced directly with SQL, since the output of the query has a nested rather than flat tabular data structure.

Nevertheless, Ultorg's nested data model is ideal for visualizing SQL queries, since database queries are themselves composed of nested structures such as joins, aggregates, and subqueries. See our SIGMOD paper[2] for details.

Data Tables, Joins, and Subqueries

As previously explained, the bold field names in the table header (Courses, Readings, etc.) indicate the hierarchy of subqueries in the perspective. Each subquery can pull data from a single data table, and each subquery can have join conditions against its parent subquery.

In the Folders sidebar, a blue arrow (Blue arrow indicating the data table that provides data for the currently selected cell.) indicates which table the data currently under the cell cursor is coming from:

The blue arrow in the Folders sidebar indicating the table providing data to the currently selected cell in the perspective editor.

Show Hierarchy

By default, the field selector popup shows both primitive fields and subqueries. By clicking the Show Hierarchy (Show Hierarchy icon) button, you can show only the hierarchy of subqueries and suggested joins, starting from the root and automatically expanded as space permits:

The Fields popup with the Show Hierarchy option enabled.

This mode serves as a quick way to visualize available foreign key relationships, and to make related tables visible. It is a compact, tree-structured version of a schema diagram.

“Via” Indications

In some cases, there may be more than one way to join a table against another. In this case a disambiguating “via” notation may be shown in grey after the name of each subquery:

The 'via' indication shown in grey text next to the name of each of two subqueries, when the subqueries have the same name.

You can rename the subqueries if desired. When names are distinct, the grey text disappears.

Two parallel subqueries pulling data from the same data table, where the names of the subqueries have been customized to disambiguate them. When the names of the subqueries are distinct, the 'via' indication is hidden.

Many-to-Many Relationships

In the database literature, a distinction is often made between one-to-many and many-to-many relationships. Two data tables have a many-to-many relationship if rows in either table can associate with any number of rows from the other table.

For example, a database of Orders and Products may associate any number of products with each order, and associate any number of orders with each product:

A perspective showing Orders, and for each order, a list of associated Products, with the junction table Line Items in the middle. Primary and foreign key values are kept visible for the benefit of the reader. This figure illustrates the concept of a many-to-many relationship

In a database schema, a many-to-many relationship is created using two one-to-many relationships and an extra table in the middle, known as the “junction table” or “join table”.

In the example above, the junction table is called “Line Items”. It holds foreign keys to Orders and Products, as well as data related to the relationship itself, such as the quantity ordered.

A diagrammatic explanation of the concept of a many-to-many relationship, and how it is typically created in a database schema by using an intermediary junction table and two one-to-many relationships.

In Ultorg, a many-to-many relationship will typically be viewed from one of its two ends. Depending on your preferred perspective, you could see, for example:

  • A list of orders, and for each order, a list of products, as shown above, or...
  • A list of products, and for each product, a list of orders.

The Move to Root action, discussed next, lets you switch between these kinds of grouping hierarchies. The Custom Group action handles other kinds of groupings.

Move to Root

The Move to Root (or Move towards Root) action is available when you open the context menu on a subquery's bold table heading. This action lets you reorganize the order of joins in your perspective, as if you had started building it from a different table.

The Move to Root action, used to move a nested subquery to the root of its perspective.

For usability reasons, the Hide Parent If Empty setting (Hide Parent If Empty icon) becomes active by default from the former root subquery. You can clear it with the Clear Filter action.

Terminology Recap

By now, we have introduced a fair amount of terminology. Here is a summary:

  • Data table: A table of raw data in a database or other data source.
  • Join: A database operation that combines data from two data tables, based on matching identifiers in specified columns.
  • One-to-many relationship: A possible result of joining two data tables, when each row in one table can match multiple rows in the other.
  • Foreign key relationship: A pre-defined suggestion of how to join two data tables, defined by the data source. Usually a one-to-many relationship.
  • Perspective (Ultorg concept): The spreadsheet-like data grid that you spend most of your time interacting with in Ultorg. Represents a database query.
  • Subquery (Ultorg concept): One level in the hierarchical structure of a perspective. Shown with a bold heading in the table header, and a subquery icon (Subquery icon) in the field selector.
    • Each subquery can pull data from one data table.
    • Each subquery represents a join against its parent subquery.
    • Subqueries contain fields which can either be primitive fields or other subqueries.