Skip to main content
Retrieves specific data from one or more tables in a database based on certain criteria. If you run multiple SELECT queries in a single script, you must them with a semicolon (;). Firebolt also supports CREATE TABLE...AS SELECT (CTAS). For more information, see CREATE TABLE…AS SELECT.

Syntax

SELECT

The SELECT list defines the columns that it returns. Each <select_expression> in the SELECT list can be either an individual expression or a wildcard.
You cannot select only partitioned or virtual columns. Selecting both partitioned or virtual columns together with regular columns is supported, but selecting only partitioned or virtual columns is not.

SELECT expression

Expressions in the SELECT list return a single value and generate one output column. You can define the column name using an explicit alias with the AS clause, or, for expressions without explicit alias, the output column name is automatically generated. Expressions can reference any column from the FROM clause, but cannot reference other columns produced by the same SELECT list. The expressions can use scalar functions, aggregate functions, window functions or subqueries, as long as they return a single value.

Example

The following code retrieves the currentscore, currentspeed, and the product of currentlevel and playterid as score_information from the playstats table:

Column name generation

Colum names for expressions in the SELECT list without an explicit alias are generated using the same name generation scheme as PostgreSQL, where column names are derived from the top-level expression node in the syntax tree, without enforcing uniqueness disambiguating duplicate names, according to the following rules:
  • Explicit aliases are always used as column names.
  • Simple column references use the name of the referenced column, dropping the table qualifier.
  • Function calls, such as count(*) or now() use the name of the function as column name.
  • Casts use the column name of the underlying expression. Note that PostgreSQL uses the internal name of the type if the underlying expression is a constant or a complex expression that would otherwise use ?column? as column name. For example, in PostgresSQL, the generated column for 1::int is int4. However, Firebolt just uses ?column?.
  • Scalar subquery expressions, such as (select now()), take the column name of the underlying projected column, computed using these rules.
  • For other complex expressions and constants ?column? is used as column name.

SELECT wildcard

Wildcards are expanded into multiple output columns based on the following rules:
  • The wildcard symbol (*) expands to include all columns in the FROM clause.
  • <table_name>.* expands to include all columns specified in the FROM clause for the table named <table_name>
  • EXCLUDE defines columns which are removed from the previous expansion.

SELECT DISTINCT

The SELECT DISTINCT statement removes duplicate rows.

SELECT ALL

The SELECT ALL statement returns all rows. SELECT ALL is the default behavior.

WITH

The WITH clause refactors subqueries so that you can define them once and reference them within the main query. This simplifies the hierarchy of the main query, enabling you to avoid using multiple nested sub-queries. In order to reference the data from the WITH clause, a name must be specified for it. This name is then treated as a temporary relation table during query execution. The primary query and the queries included in the WITH clause are all run at the same time; WITH queries are evaluated only once every time the main query runs, even if the clause is referred to by the main query more than once.

Materialized common table expressions

The query hint MATERIALIZED or NOT MATERIALIZED controls whether common table expressions (CTEs) produce an internal results table that is cached in engine RAM (MATERIALIZED) or calculated each time the sub-query runs. NOT MATERIALIZED is the default. MATERIALIZED must be specified explicitly. Materialized results can be accessed more quickly in some circumstances. By using the proper materialization hint, you can control when a CTE gets materialized and improve query performance. We recommend the MATERIALIZED hint to improve query performance in the following circumstances:
  • The CTE is reused at the main query level more than once.
  • The CTE is computationally expensive, producing a relatively small number of rows.
  • The CTE calculation is independent of the main query, and no external optimizations from the main table are needed for it to be fast.
  • The materialized CTE fits into the nodes’ RAM.

Syntax

Example

The following example retrieves all players who have subscribed to receive the game newsletter, having the results of the WITH query in the temporary table nl_subscribers. The results of the main query then list the nickname and email for those customers, sorted by nickname.

Reusable common table expressions

Even stronger than MATERIALIZED, you can mark a CTE as reusable. This means that the results of the CTE stay cached in engine RAM and can be reused across different queries. For MATERIALIZED REUSABLE CTEs, Firebolt materializes the full CTE results of the query, possibly even including unused columns that could otherwise be pruned. This ensures that different queries specifying the same CTE can use the same cache entry, even if they need different columns. Refer to Understand query performance and subresults for more details on the subresult cache.
REUSABLE can only be applied if the result of the CTE can be cached, which can be subtle in some cases. Refer to Limitations for more details. Query execution of a CTE that is explicitly marked as REUSABLE fails if it contains statements that cannot be cached.
You can view information about the currently cached materialized CTEs using the information_schema.engine_caches view:

FROM

Use the FROM clause to list the tables and any relevant join information and functions necessary for running the query.

Syntax

Example

In the following example, the query retrieves all entries from the players table for which the agecategory value is “56+”.

FROM first

Firebolt allows using the FROM clause before the SELECT clause. The previous example can also be written as follows:
You can also omit the SELECT clause and use only the FROM clause in the query as shown in the following code example:
The previous code example is equivalent to the following:

Reading files directly

A <from_item> can be a string literal holding a file path or URL. Firebolt reads it with READ_FILES, inferring the format from the object it points at.
This is shorthand for SELECT * FROM READ_FILES('s3://.../levels.parquet'). It works for local paths and object storage that needs no credentials; to pass credentials or a LOCATION, call READ_FILES(...) directly.

JOIN

A JOIN operation combines rows from two data sources, such as tables or views, and creates a new table of combined rows that can be used in a query. JOIN operations can be used with an ON clause for conditional logic or a USING clause to specify columns to match.

JOIN with ON clause syntax

JOIN with USING clause syntax

JOIN types

The type of JOIN operation specifies which rows are included between two specified tables. If unspecified, JOIN defaults to INNER JOIN. JOIN types include:

Examples

The following JOIN examples use two tables, level_one_players and level_two_players. These tables are created and populated with data as follows.
The tables and their data are shown as follows:

INNER JOIN example

The INNER JOIN example below includes only the rows where the nickname and currenscore values match.
The previous query is equivalent to the following:
Returns

LEFT OUTER JOIN example

The following LEFT OUTER JOIN example includes all nickname values from the level_one_players table. Any rows with no matching value in the level_two_players table return NULL.
Returns

RIGHT OUTER JOIN example

The following RIGHT OUTER JOIN example includes all nickname values from level_two_players. Any rows with no matching values in the level_one_players table return NULL.
Returns

FULL OUTER JOIN example

The following FULL OUTER JOIN example includes all values from num_test and num_test2. Any rows with no matching values return NULL.
Returns

CROSS JOIN example

A CROSS JOIN produces a table with every combination of row values in the specified columns. The following example uses two tables with player information, beginner_player and intermediate_player, each with a single level column. The tables contain the following data: The following CROSS JOIN example produces a table of every possible pairing of these rows.
Returns

UNNEST

UNNEST is a table-valued function (TVF) that transforms an input row containing an array into a set of rows. The output table repeats rows of the input table for every element of the array. Every array element is attached to one of the output rows. If the input array is empty, the corresponding row is eliminated.

Syntax - FROM Clause

Using TVFs such as UNNEST is permitted in FROM clauses as follows:
The previous query performs a lateral join onto the result of the UNNEST operation. However, the LATERAL keyword is optional.

Syntax - SELECT Clause

When unnesting a single column, the TVF can also be invoked directly in the SELECT clause.

Example

The example is based on the following table:
Assume the table was populated and contains the following values: The following query with UNNEST:
Returns the following result: The above query can be rewritten to invoke UNNEST in the SELECT clause:

WHERE

Use the WHERE clause to define conditions for the query in order to filter the query results. When included, the WHERE clause always follows the FROM clause as part of a command such as SELECT.

Syntax

Example

In the following example, the query retrieves all entries from the customers table for which the region value is “EMEA”.
The following query retrieves users who registered after August 30, 2020 from the players table:
The following query retrieves users who registered after August 30, 2020:

GROUP BY

The GROUP BY clause groups together input rows. Multiple input rows which have same values of expressions in the GROUP BY clause become a single row in the output. GROUP BY is typically used in conjunction with aggregate functions such as SUM and MIN. Query with GROUP BY clause and without aggregate functions is equivalent to SELECT DISTINCT.

Syntax

Example

In the following example, the retrieved results are grouped by the nickname column, and then by the email column.
If the expression in GROUP BY clause is exactly the same as in the SELECT list, then its position can be used instead.
The GROUP BY clause must include all expressions in the SELECT list that do not use aggregate functions. It may include expressions which are not part of SELECT list.
The following will cause an error, since SELECT list has an expression which is not an aggregate function, and it is not listed in GROUP BY clause.

GROUP BY ALL

For the common case of GROUP BY clause repeating all the non-aggregate function expressions in the SELECT list, it is possible to use GROUP BY ALL syntax. It will automatically group by all non-aggregate functions expressions from the SELECT list.

GROUP BY GROUPING SETS

GROUPING SETS are an extension of GROUP BY that allow specifying multiple GROUP BY groups in one statement. Note that the total number of all grouping sets generated by the combination of all GROUPING SETS, ROLLUP, and CUBE clauses cannot exceed 4096.
Syntax
Example
We want to determine the number of players in each age category, segmented by whether they are subscribed to the newsletter or not. Additionally, we want to find the total number of players per age category and the overall amount of players.
The grouping sets in this example are (agecategory, issubscribedtonewsletter), agecategory, and (). Each set corresponds to one of the groups described earlier. The query using these grouping sets is equivalent to the following:
Note that the empty grouping set () corresponds to a query with an aggregate function but without a GROUP BY clause. For columns not included in a particular grouping set, their values are set to NULL in the resulting rows.

GROUP BY ROLLUP

ROLLUP is syntactic sugar for a common GROUPING SETS use case. It takes n arguments and produces the n+1 prefixes of these arguments as grouping sets. Note that the total number of all grouping sets generated by the combination of all GROUPING SETS, ROLLUP, an CUBE clauses cannot exceed 4096.
Syntax
Example
is expanded to

GROUP BY CUBE

CUBE is syntactic sugar for a common GROUPING SETS use case. It takes n arguments and produces the possible 2n subsets as grouping sets. Note that the total number of all grouping sets generated by the combination of all GROUPING SETS, ROLLUP, an CUBE clauses cannot exceed 4096. This means that a GROUP BY clause with a sinle CUBE expression can have at most 12 = log2(4096) arguments.
Syntax
Example
is expanded to

HAVING

The HAVING clause is used in conjunction with the GROUP BY clause, and is computed after computing the GROUP BY clause and aggregate functions. HAVING is used to further eliminate groups that don’t satisfy the <condition> by filtering the GROUP BY results.

Syntax

QUALIFY

The QUALIFY clause filters the results of window functions. It is conceptually equivalent to what WHERE is for base rows and HAVING is for grouped rows: QUALIFY operates on rows after the window functions have been evaluated. This lets you filter on a window function directly, without wrapping the query in a subquery. QUALIFY is evaluated in the following logical order: FROMWHEREGROUP BYHAVING → window functions → QUALIFYSELECT projection → ORDER BYLIMIT. The query must contain at least one window function — either inside the QUALIFY predicate itself or somewhere in the SELECT list. Within QUALIFY you can reference base columns, group-by expressions, aggregates, window functions inline, and any aliases defined in the SELECT list.

Syntax

Examples

Keep only the highest-paid employee per department, using ROW_NUMBER:
Reference a window function via its SELECT list alias:
Combine QUALIFY with WHERE, GROUP BY, and HAVING. WHERE filters input rows; GROUP BY + HAVING aggregate and filter groups; the window function then runs over the grouped output and QUALIFY filters again:

Notes

  • QUALIFY must appear after WHERE, GROUP BY, HAVING, and any WINDOW clause, and before ORDER BY / LIMIT.
  • A query whose QUALIFY predicate references no window function and whose SELECT list contains no window function is rejected at validation time.
  • When SELECT * is combined with QUALIFY, the star expands only to the underlying table’s columns; window functions used solely inside the QUALIFY predicate are not added to the projection.

UNION [ALL] [BY NAME]

The UNION operator combines the results of two or more SELECT statements into a single query.
  • UNION combines data by column position with duplicate elimination.
  • UNION ALL combines data by column position without duplicate elimination.
  • UNION BY NAME combines data by column name with duplicate elimination.
  • UNION ALL BY NAME combines data by column name without duplicate elimination.
Without BY NAME, the same number of columns must be selected by all SELECT statements on both sides of the UNION. Data types of matching columns (at the same position or having the same name if in BY NAME mode) must have a common data type. Multiple UNION clauses are processed from left to right. Use parentheses to define an explicit order for processing. In BY NAME mode:
  • Columns with the same identifiers are matched and combined. Quoted identifiers are case-sensitive as usual.
  • If a column exists in one result set but not the other, it is filled with NULL values in the combined result set for each row where it’s missing.
  • The order of columns in the combined result set is determined by the order of columns from left to right, as they are first encountered.
  • The combination cannot be performed by name in the following cases:
    • one result set has duplicated column names
    • some column does not have a name

Syntax

ORDER BY

The ORDER BY clause sorts a result set by one or more output expressions. ORDER BY is evaluated as the last step after any GROUP BY or HAVING clause. ASC and DESC determine whether results are sorted in ascending or descending order. When the clause contains multiple expressions, the result set is sorted according to the first expression. Rows with the same values for the first expression are then sorted by the second expression, and this process continues for subsequent expressions. The NULLS FIRST and NULLS LAST options can be used to determine whether NULL values appear before or after non-NULL values in the sort order. By default, NULL values are considered greater than any non-NULL value. NULLS FIRST is the default for descending order, and NULLS LAST is the default for ascending order.

Syntax

ORDER BY ALL

ORDER BY ALL sorts by every column in the SELECT list, from left to right. It is equivalent to listing each output column by its ordinal position, so SELECT a, b, c FROM t ORDER BY ALL is the same as SELECT a, b, c FROM t ORDER BY 1, 2, 3. With SELECT *, it sorts by every expanded column. ORDER BY * is an accepted synonym for ORDER BY ALL.
A single ASC/DESC and NULLS FIRST/NULLS LAST modifier after ALL applies to every column. This differs from the positional form, where each modifier binds only to its own key: ORDER BY ALL DESC sorts all columns descending and is equivalent to ORDER BY 1 DESC, 2 DESC, 3 DESC, not to ORDER BY 1, 2, 3 DESC (which sorts descending by the last key only).

LIMIT

The LIMIT clause restricts the number of rows that are included in the result set.

Syntax

OFFSET

The OFFSET clause specifies a non-negative number of rows that are skipped before returning results from the query.

Syntax

VALUES Lists

VALUES creates an in-memory “constant table” with one or multiple rows for use in queries. Each parenthesized list of expressions represents a row. All rows must have the same number of elements, and corresponding elements in each row must have compatible data types. As an example:

Rows: 3Execution time: 5ms

is effectively equivalent to:
Syntactically, VALUES can be used anywhere a SELECT is allowed.

Syntax

WITH settings

List of query-specific settings overrides.

Syntax