Subqueries
Subqueries enable the reuse of results of other queries.
SQreamDB supports relational (also called derived table) subqueries, which appear as SELECT queries as part of a table expression.
SQreamDB also supports Common Table Expressions, which are a form of subquery. With CTEs, a subquery can be named for reuse in a query.
Note
You may include an unlimited number of subqueries within a single SQL statement, and you can also nest subqueries to an unlimited depth
Table Subqueries
The following is an example of table named nba with the following structure:
CREATE TABLE nba
(
"Name" TEXT,
"Team" TEXT,
"Number" TINYINT,
"Position" TEXT,
"Age" TINYINT,
"Height" TEXT,
"Weight" REAL,
"College" TEXT,
"Salary" FLOAT
);
To see the table contents, click Download nba.csv:
Name |
Team |
Number |
Position |
Age |
Height |
Weight |
College |
Salary |
|---|---|---|---|---|---|---|---|---|
Avery Bradley |
Boston Celtics |
0.0 |
PG |
25.0 |
6-2 |
180.0 |
Texas |
7730337.0 |
Jae Crowder |
Boston Celtics |
99.0 |
SF |
25.0 |
6-6 |
235.0 |
Marquette |
6796117.0 |
John Holland |
Boston Celtics |
30.0 |
SG |
27.0 |
6-5 |
205.0 |
Boston University |
|
R.J. Hunter |
Boston Celtics |
28.0 |
SG |
22.0 |
6-5 |
185.0 |
Georgia State |
1148640.0 |
Jonas Jerebko |
Boston Celtics |
8.0 |
PF |
29.0 |
6-10 |
231.0 |
5000000.0 |
|
Amir Johnson |
Boston Celtics |
90.0 |
PF |
29.0 |
6-9 |
240.0 |
12000000.0 |
|
Jordan Mickey |
Boston Celtics |
55.0 |
PF |
21.0 |
6-8 |
235.0 |
LSU |
1170960.0 |
Kelly Olynyk |
Boston Celtics |
41.0 |
C |
25.0 |
7-0 |
238.0 |
Gonzaga |
2165160.0 |
Terry Rozier |
Boston Celtics |
12.0 |
PG |
22.0 |
6-2 |
190.0 |
Louisville |
1824360.0 |
Scalar Subqueries
The following are examples of scalar subqueries.
Simple Subquery
SELECT
AVG("Age")
FROM
(
SELECT
"Name",
"Team",
"Age"
FROM
nba
WHERE
"Height" > '7-0'
);
avg
---
26
Combining a Subquery with a Join
SELECT
*
FROM
(
SELECT
"Name"
FROM
nba
WHERE
"Height" > '7-0'
) AS t(name),
nba AS n
WHERE
n."Name" = t.name;
name | Name | Team | Number | Position | Age | Height | Weight | College | Salary
-------------------+--------------------+------------------------+--------+----------+-----+--------+--------+------------+---------
Alex Len | Alex Len | Phoenix Suns | 21 | C | 22 | 7-1 | 260 | Maryland | 3807120
Alexis Ajinca | Alexis Ajinca | New Orleans Pelicans | 42 | C | 28 | 7-2 | 248 | \N | 4389607
Boban Marjanovic | Boban Marjanovic | San Antonio Spurs | 40 | C | 27 | 7-3 | 290 | \N | 1200000
Kristaps Porzingis | Kristaps Porzingis | New York Knicks | 6 | PF | 20 | 7-3 | 240 | \N | 4131720
Marc Gasol | Marc Gasol | Memphis Grizzlies | 33 | C | 31 | 7-1 | 255 | \N | 19688000
Meyers Leonard | Meyers Leonard | Portland Trail Blazers | 11 | PF | 24 | 7-1 | 245 | Illinois | 3075880
Roy Hibbert | Roy Hibbert | Los Angeles Lakers | 17 | C | 29 | 7-2 | 270 | Georgetown | 15592217
Rudy Gobert | Rudy Gobert | Utah Jazz | 27 | C | 23 | 7-1 | 245 | \N | 1175880
Salah Mejri | Salah Mejri | Dallas Mavericks | 50 | C | 29 | 7-2 | 245 | \N | 525093
Spencer Hawes | Spencer Hawes | Charlotte Hornets | 0 | PF | 28 | 7-1 | 245 | Washington | 6110034
Tibor Pleiss | Tibor Pleiss | Utah Jazz | 21 | C | 26 | 7-3 | 256 | \N | 2900000
Timofey Mozgov | Timofey Mozgov | Cleveland Cavaliers | 20 | C | 29 | 7-1 | 275 | \N | 4950000
Tyson Chandler | Tyson Chandler | Phoenix Suns | 4 | C | 33 | 7-1 | 240 | \N | 13000000
Walter Tavares | Walter Tavares | Atlanta Hawks | 22 | C | 24 | 7-3 | 260 | \N | 1000000
WITH subqueries
See Common Table Expressions for more information.
WITH nba_ct AS (
SELECT
"Name",
"Team"
FROM
nba
WHERE
"College" = 'Connecticut'
),
nba_az AS (
SELECT
"Name",
"Team"
FROM
nba
WHERE
"College" = 'Arizona'
)
SELECT
*
FROM
nba_az
JOIN nba_ct ON nba_ct."Team" = nba_az."Team";
Name | Team | name0 | team0
----------------+-----------------+----------------+----------------
Stanley Johnson | Detroit Pistons | Andre Drummond | Detroit Pistons
Aaron Gordon | Orlando Magic | Shabazz Napier | Orlando Magic
IN Subqueries and Multi-Column IN
IN and NOT IN accept a subquery as their right side, and accept a row of several values as their left side.
Comparing a row checks every position at once, so (a, b) IN (...) matches only where a and b match the
same row of the list or subquery.
<expression> [NOT] IN ( <select_statement> )
( <expression1>, <expression2>, ... ) [NOT] IN ( <select_statement> )
( <expression1>, <expression2>, ... ) [NOT] IN ( ( <value1>, <value2>, ... ), ( <value1>, <value2>, ... ), ... )
The row can also be written as ROW(a, b), and a list of rows as VALUES (1, 2), (3, 4).
These forms may be used in the WHERE clause of a SELECT, DELETE, or UPDATE statement, and as a
true-or-false value in a select list or a CASE expression.
The examples below use the following table:
CREATE OR REPLACE TABLE Sales (
ProductName TEXT NOT NULL,
SalesDate DATE NOT NULL,
Revenue NUMERIC(10, 2) NOT NULL
);
INSERT INTO Sales VALUES
('Product A', '2024-01-01', 100.00),
('Product B', '2024-01-01', 150.00),
('Product A', '2024-01-02', 120.00),
('Product B', '2024-01-02', 180.00),
('Product C', '2024-01-01', 200.00);
Filtering on a combination of columns against a list of rows:
SELECT * FROM Sales
WHERE (ProductName, SalesDate) IN (('Product A', '2024-01-01'), ('Product B', '2024-01-02'));
productname | salesdate | revenue
------------+------------+---------
Product A | 2024-01-01 | 100.00
Product B | 2024-01-02 | 180.00
Returning each product’s most recent sale, by comparing two columns against a subquery:
SELECT * FROM Sales
WHERE (ProductName, SalesDate) IN (
SELECT ProductName, MAX(SalesDate) FROM Sales GROUP BY ProductName
);
productname | salesdate | revenue
------------+------------+---------
Product C | 2024-01-01 | 200.00
Product A | 2024-01-02 | 120.00
Product B | 2024-01-02 | 180.00
A long list of rows is compiled as a join against the listed rows, the same as a long single-column IN list, so
lists of many thousands of rows remain practical. The point at which this happens is set by
inListJoinThreshold, counted in comparisons: one per column in each row.
NOT IN and nullable columns
IN and NOT IN over columns declared NOT NULL compile into a single join. NOT IN with a subquery is
slower where the compared columns can be NULL, because a NULL on either side may match any value: every
compared column that can be NULL, on either side of the IN, doubles the number of joins. The
inSubqueryNullJoinLimit flag caps that number, by default at 8, which allows up to
three nullable columns. A statement over the limit fails with an error naming the flag. Declaring the compared columns
NOT NULL where the data allows it avoids the cost.
Limitations
The subquery must return as many columns as the row on the left side has values. Otherwise the statement returns
The IN subquery returns 1 columns, but the left side of IN has 2 values.Every row in a list must have as many values as the left side. Otherwise the statement returns
Every item of this IN list must be a row of 2 values.A single value on the left side cannot be compared with a list of rows. This returns
This IN list item is a row, but the left side of IN is a single value.Rows cannot be nested inside rows.
A subquery cannot supply the value list of a
PIVOTorPIVOT ARRAY.