# Iteration Queries

# Introduction

Relationship Visualizer Version 10.0 added support for iterative SQL queries, allowing a query to use a result value produced by a previous query.

Iterate vs. Enumerate?

Within Relationship Visualizer, a distinction is made between iteration and enumeration:

  • Iterate refers to stepping through each item in a result set one by one โ€” without tracking position or count. Each item becomes input for a follow-up SQL query.

  • Enumerate refers to iteration with explicit tracking of position, index, or count, using a counter that increments from a starting value to a stopping value. Enumeration is used when the logic depends on the numeric sequence itself. For example, โ€œgenerate rows from 1 to 12,โ€ โ€œstep through values by 5,โ€ or โ€œfill in missing years.โ€

# Scenario

You want to build a timeline graph showing the evolution of Unix shells. Assume you have an Excel workbook with a worksheet named shells that contains two columns: one for the year and one for a piece of information associated with that year. In this example, each year is linked to the names of Unix shells introduced during that time. The goal is to process the data so that all shells from the same year appear on the same rank in the final visualization.

The data appears as:

Year Shell
future ksh-POSIX
future POSIX
1972 Thompson
1976 Bourne
1976 Mashey
1978 Formshell
1978 csh
1980 esh
1980 vsh
1982 ksh
1982 System-V
1984 v9sh
1984 tcsh
1986 ksh-i
1988 rc
1988 KornShell
1988 Perl
1990 Bash
1990 tcl

# Step By Step Guide

# Step 1 โ€“ Place all the values for a given year in a subgroup

Relationship Visualizer includes a SQL extension that lets you define subgroups directly within a query. This is useful when you want multiple items to share the same position or rank in the final diagram. In this scenario, each year may have several shells associated with it, and we want all shells from the same year to appear together.

You can create these subgroups using the CREATE RANK clause, shown below:

TRUE AS [CREATE RANK]
1

If we want to place all the shells from 1988 on the same rank, we would write the query as follows:

SELECT [Shell] AS [ITEM], 
       TRUE    AS [CREATE RANK], 
       'same'  AS [RANK] 
FROM  [shells$] 
WHERE [YEAR] = '1988'
1
2
3
4
5

This will insert the following subgroup into the DOT source:

{ rank="same"; "1988"; "KornShell"; "Perl"; "rc"; }
1

# Step 2 โ€“ Get a list of unique years

The previous approach works for a single year, but we want to repeat the process for every year in the dataset without hardโ€‘coding values into the SQL. The next step is to retrieve a list of all years, removing duplicates by using the standard SELECT DISTINCT command:

SELECT DISTINCT [Year] AS [ID] FROM [shells$]
1
ID
future
1972
1976
1978
1980
1982
1984
1986
1988
1990

# Step 3 โ€“ Iterate the unique years, generating a subgroup for each year

This step combines Step 2 with Step 1. For this step, we supply an additional SQL extension flag introduced in Version 10.

TRUE AS [ITERATE]
1

This flag tells Relationship Visualizer that the query is a special iterative query. It instructs the engine to run two SQL statements: the first generates the list of IDs, and the second is executed once for each ID in that list.

You pass the queries using the column names

  • [SQL FOR ID]
  • [SQL FOR DATA]

The [SQL FOR ID] query producing the ID values is:

SELECT DISTINCT [Year] AS [ID] FROM [shells$]
1

The [SQL FOR DATA] query to iterate the ID values is:

SELECT [Shell] AS [ITEM], TRUE AS [CREATE RANK], ''same'' AS [RANK] FROM [shells$] WHERE [YEAR] = '{ID}'
1

Combining these into a single SQL statement looks like this. Note that because the SQL statements are being passed as strings, any string values inside the queryโ€”such as '{ID}'โ€”must be escaped with doubled single quotes, like ''{ID}''.

SELECT 
  'SELECT DISTINCT [Year] AS [ID] FROM [shells$]' AS [SQL FOR ID],
  'SELECT [Shell] AS [ITEM], TRUE AS [CREATE RANK], ''same'' AS [RANK] FROM [shells$] WHERE [YEAR] = ''{ID}'' ' AS [SQL FOR DATA],
  TRUE AS [ITERATE]
1
2
3
4

Relationship Visualizer will loop through the ID list produced by the first query. Each ID value is substituted into the second query wherever the {ID} placeholder appears. The modified query is then executed, and the results are written to the data worksheet.

Using the sample data above, this pair of SQL statements adds the following entries to the data worksheet:

{ rank="same"; "Thompson"; }
{ rank="same"; "Bourne"; "Mashey"; }
{ rank="same"; "csh"; "Formshell"; }
{ rank="same"; "esh"; "vsh"; }
{ rank="same"; "ksh"; "System-V"; }
{ rank="same"; "tcsh"; "v9sh"; }
{ rank="same"; "ksh-i"; }
{ rank="same"; "KornShell"; "Perl"; "rc"; }
{ rank="same"; "Bash"; "tcl"; }
{ rank="same"; "ksh-POSIX"; "POSIX"; }
1
2
3
4
5
6
7
8
9
10

# Step 4 โ€“ Add the year to the subgroups

The subgroups based on year have been created, but notice that the year itself is not included. How do we know which shells belong to which years?

In SQL, the UNION operator functions like an โ€œANDโ€ that allows you to run additional queries and return the unique combined results.

The following statement can supply the year:

SELECT [Year] AS [ITEM] WHERE [YEAR] = '{ID}'
1

UNION has a restriction that all SQL statements must return the same list of fields. To comply with this rule, the statement becomes:

SELECT [Year]  AS [ITEM], TRUE AS [CREATE RANK], 'same' AS [RANK] FROM [shells$] WHERE [YEAR] = '{ID}'
1

The UNION is added to the statement as follows:

SELECT 
  'SELECT DISTINCT [Year] AS [ID] FROM [shells$]' AS [SQL FOR ID],
  'SELECT [Year]  AS [ITEM], TRUE AS [CREATE RANK], ''same'' AS [RANK] FROM [shells$] WHERE [YEAR] = ''{ID}'' 
      UNION
   SELECT [Shell] AS [ITEM], TRUE AS [CREATE RANK], ''same'' AS [RANK] FROM [shells$] WHERE [YEAR] = ''{ID}'' ' AS [SQL FOR DATA],
  TRUE AS [ITERATE]
1
2
3
4
5
6

When the query is run, the following subgroups are produced:

{ rank="same"; "1972"; "Thompson"; }
{ rank="same"; "1976"; "Bourne"; "Mashey"; }
{ rank="same"; "1978"; "csh"; "Formshell"; }
{ rank="same"; "1980"; "esh"; "vsh"; }
{ rank="same"; "1982"; "ksh"; "System-V"; }
{ rank="same"; "1984"; "tcsh"; "v9sh"; }
{ rank="same"; "1986"; "ksh-i"; }
{ rank="same"; "1988"; "KornShell"; "Perl"; "rc"; }
{ rank="same"; "1990"; "Bash"; "tcl"; }
{ rank="same"; "future"; "ksh-POSIX"; "POSIX"; }
1
2
3
4
5
6
7
8
9
10

Now the subgroups contain both the year and the shells introduced during that year.

# Step 5 โ€“ Build a timeline

Next, we want to build a timeline. For this, we use another Relationship Visualizer SQL extension.

Similar to the CREATE RANK keyword, there is a CREATE EDGES keyword, specified as:

TRUE AS [CREATE EDGES]
1

When this query runs, it generates a set of rows in the data worksheet that create an edge from each item to the next. If the data contains a, b, c, the resulting edges will be a โ†’ b โ†’ c.

The single statement:

SELECT DISTINCT [Year] AS [ITEM], 
       TRUE            AS [CREATE EDGES] 
FROM   [Shells$] 
WHERE  [Year] IS NOT NULL 
ORDER BY [Year] ASC
1
2
3
4
5

produces the following chain of edges from year to year.

# Step 6 โ€“ Connect the subgroups to the timeline

This step combines the SQL statements from Step 5 and Step 6. The resulting graph becomes:

# Step 7 โ€“ Depict relationships between items

Our data also includes a worksheet named evolution, which tells us which shells preceded other shells. The data looks as follows:

From Shell To Shell
Thompson Bourne
Thompson Mashey
Thompson csh
Bourne v9sh
Bourne ksh
Bourne esh
Bourne vsh
Bourne Formshell
Bourne Bash
Bourne System-V
Formshell ksh
csh ksh
ksh ksh-i
csh tcsh
System-V POSIX
v9sh rc
KornShell ksh-POSIX
KornShell POSIX
KornShell Bash
esh ksh
vsh ksh
ksh-i KornShell
ksh-i Bash

It is a simple change to add the following SQL statement:

SELECT [From Shell] AS [ITEM], 
       [To Shell]   AS [RELATED ITEM] 
FROM   [evolution$]

1
2
3
4

which changes the graph to:

# Step 8 โ€“ Add style and adjust layout

The last step is to create styles and apply them to the nodes and edges. This is standard Relationship Visualizer work, so it wonโ€™t be detailed here. Refer to the sample spreadsheet if youโ€™d like to explore the styling options.

The fully styled graph becomes:

# Try it Yourself

This example is included in the samples in the Relationship Visualizer zip file in the directory 16 - Using SQL - Iteration.


Like this tool? Buy me a coffee! โ˜• (opens new window)