Skip to main content
The simple SELECT clause is the main SQL syntax to read and process existing data. When used as a stand-alone statement, the simple SELECT clause is also called “the SELECT statement”. However, it is also a that can be combined with other constructs to form more complex .

Synopsis

simple_select_clause syntax diagram
The simple SELECT clause also has other applications not covered here, such as executing like SELECT current_timestamp();.

Required privileges

The user must have the SELECT on the tables used as operands.

Parameters

ParameterDescription
DISTINCT or ALLSee Eliminate Duplicate Rows.
DISTINCT ON ( a_expr [, ...] )DISTINCT ON followed by a list of within parentheses. See Eliminate Duplicate Rows.
target_elemA to compute a column in each result row.

* automatically retrieves all columns. If a column has the , it is not returned with *.

If target_elem contains an , a GROUP BY clause can be used to further control the aggregation.
table_refThe you want to retrieve data from.

Using two or more table expressions in the FROM sub-clause, separated with a comma, is equivalent to a expression.
AS OF SYSTEM TIME timestampRetrieve data as it existed .

Note: Because AS OF SYSTEM TIME returns historical data, your reads might be stale.
WHERE a_exprOnly retrieve rows that return TRUE for a_expr, which must be a that returns Boolean values using columns (e.g., <column = <value>).
GROUP BY a_exprGroup results on one or more columns.

When an follows SELECT as a target_elem, or HAVING as an a_expr, you can create aggregate groups on column groupings listed after GROUP BY.
You can group columns by an alias (i.e., a label assigned to the column with an AS clause) rather than the column name.
If aggregate groups are created on a full primary key, any column in the table can be selected as a target_elem, or specified in a HAVING clause.
If a selected column is in a , and the column references a higher scope, the column does not need to be included in the GROUP BY clause (if one exists).

Using a GROUP BY clause in a statement without an aggregate function is equivalent to using a DISTINCT ON clause on the grouping columns.
HAVING a_exprOnly retrieve aggregate function groups that return TRUE for a_expr, which must be a that returns Boolean values using an aggregate function (e.g., <aggregate function = <value>).

HAVING works like the WHERE clause, but for aggregate functions.
WINDOW window_definition_listA list of .

Eliminate duplicate rows

The DISTINCT subclause specifies to remove duplicate rows. By default, or when ALL is specified, SELECT returns all the rows selected, without removing duplicates. When DISTINCT is specified, duplicate rows are eliminated. Without ON, two rows are considered duplicates if they are equal on all the results computed by SELECT. With ON, two rows are considered duplicates if they are equal only using the listed with ON. When two rows are considered duplicates according to DISTINCT ON, the values from the first FROM row in the order specified by are used to compute the remaining target expressions. If ORDER BY is not specified, CockroachDB will pick any one of the duplicate rows as first row, non-deterministically.

Examples

Setup

To follow along, run to start a temporary, in-memory cluster with the sample dataset preloaded:

Choose columns

Retrieve specific columns

Retrieve specific columns by naming them in a comma-separated list:

Retrieve all columns

Retrieve all columns by using *:

Filter rows

Filter on a single condition

Filter rows with expressions that use columns and return Boolean values in the WHERE clause:

Filter on multiple conditions

To use multiple WHERE filters join them with AND or OR. You can also create negative filters with NOT:

Filter values with a list

Using WHERE <column> IN (<comma separated list of values) performs an OR search for listed values in the specified column:

Select distinct rows

Columns without the or constraints can have multiple instances of the same value:
Using DISTINCT, you can remove all but one instance of duplicate values from your retrieved data:

Rename columns in output

Instead of outputting a column’s name in the retrieved table, you can change its label using AS:
This does not change the name of the column in the table. To do that, use .

Search for string values

Search for partial matches in columns using LIKE, which supports the following wildcard operators:
  • % matches 0 or more characters.
  • _ matches exactly 1 character.
For example:

Aggregate functions

perform calculations on retrieved rows.

Perform aggregate function on entire column

By using an aggregate function as a target_elem, you can perform the calculation on the entire column.
You can also use the retrieved value as part of an expression. For example, you can use the result in the WHERE clause to select additional rows that were not part of the aggregate function itself:

Perform aggregate function on retrieved rows

By filtering the statement, you can perform the calculation only on retrieved rows:

Filter columns fed into aggregate functions

You can use FILTER (WHERE <Boolean expression>) in the target_elem to filter which rows are processed by an aggregate function; those that return FALSE or NULL for the FILTER clause’s Boolean expression are not fed into the aggregate function:

Create aggregate groups

Instead of performing aggregate functions on an the entire set of retrieved rows, you can split the rows into groups and then perform the aggregate function on each of them. When creating aggregate groups, each column selected as a target_elem must be included in a GROUP BY clause. For example:
If the group is created on a primary key column, any column in the table can be selected as a target_elem. If a selected column is in a that references a higher scope, a GROUP BY clause is not needed.

Filter aggregate groups

To filter aggregate groups, use HAVING, which is the equivalent of the WHERE clause for aggregate groups, which must evaluate to a Boolean value. For example:

Use aggregate functions in having clause

Aggregate functions can also be used in the HAVING clause without needing to be included as a target_elem. For example:

Order aggregate function input rows by column

Non-commutative aggregate functions are sensitive to the order in which the rows are processed in the surrounding SELECT clause. To specify the order in which input rows are processed, you can add an clause within the function argument list. For example, suppose you want to create an array of name values, ordered alphabetically, and grouped by city. You can use the following statement to do so:
You can also order input rows using a column different than the input row column. The following statement returns an array of revenue values from high-revenue rides, ordered by ride end_time:
If you include multiple aggregate functions in a single SELECT clause, you can order the input rows of the multiple functions on different columns. For example:

Group by an alias

If a query includes an alias (i.e., a label assigned to the column with an AS clause), you can group the aggregations in the query by the alias rather than by the column name. For example:

Select from a specific index

By using the explicit index annotation, you can override CockroachDB’s index selection and use a specific when reading from a named table.
Index selection can impact , but does not change the result of a query.
Force index scan
To force a scan of a specific index:
This is equivalent to the longer expression:
Force reverse scan
To force a reverse scan of a specific index:
Forcing a reverse scan can help with . To choose an index and its scan direction:
where the optional DIRECTION is either ASC (ascending) or DESC (descending). When a direction is specified, that scan direction is forced; otherwise the is free to choose the direction it calculates will result in the best performance. You can verify that the optimizer is choosing your desired scan direction using . For example, given the table
you can check the scan direction with:

Force inverted index scan

To force a scan of any of the hinted table:
The FORCE_INVERTED_INDEX hint does not allow specifying an inverted index. If no query plan can be generated, the query will result in the error:
Force partial index scan
To force a , your statement must have a WHERE clause that implies the partial index filter.
Force partial GIN index scan
To force a scan, your statement must have a WHERE clause that:
  • Implies the partial index.
  • Constrains the GIN index scan.
Prevent full scan
  • To prevent the optimizer from planning a full scan for a specific table, specify the NO_FULL_SCAN index hint. For example:
  • To prevent a full scan of a for a specific table, you must specify NO_FULL_SCAN in combination with the index name using . For example:
    This forces a constrained scan of the partial index. If a constrained scan of the partial index is not possible, an error is returned.
For other ways to prevent full scans, refer to .

Select historical data (time-travel)

CockroachDB lets you find data as it was stored at a given point in time using AS OF SYSTEM TIME with various . This can be also advantageous for performance. For more details, see .

Advanced uses of SELECT clauses

CockroachDB supports numerous ways to combine results from SELECT clauses together. See for details. A few examples follow.

Sorting and limiting query results

To order the results of a SELECT clause or limit the number of rows in the result, you can combine it with ORDER BY or LIMIT / OFFSET to form a or . See and for more details.

Combining results from multiple queries

Results from two or more queries can be combined together as follows:
  • Using to combine rows according to conditions on specific columns.
  • Using to combine rows using inclusion/exclusion rules.

Row-level locking for concurrency control with SELECT FOR UPDATE

SELECT ... FOR UPDATE exclusively locks the rows returned by a selection query, such that other transactions trying to access those rows must wait for the transaction that locked the rows to commit or rollback. SELECT ... FOR UPDATE can be used to:
  • Strengthen the isolation of a transaction. If you need to read and later update a row within a transaction, use SELECT ... FOR UPDATE to acquire an exclusive lock on the row. This guarantees data integrity between the transaction’s read and write operations. For details, see .
  • Order transactions by controlling concurrent access to one or more rows of a table. These other transactions are placed into a queue based on when they tried to read the values of the locked rows. Because this queueing happens during the read operation, the thrashing that would otherwise occur if multiple concurrently executing transactions attempt to SELECT the same data and then UPDATE the results of that selection is prevented. By preventing thrashing, SELECT ... FOR UPDATE also prevents transaction retries that would otherwise occur due to . As a result, using SELECT ... FOR UPDATE leads to increased throughput and decreased tail latency for contended operations.
Note that using SELECT ... FOR UPDATE does not completely eliminate the chance of . These errors can also arise due to . To eliminate the need for application-level retry logic, in addition to SELECT FOR UPDATE your application also needs to use a .
By default, CockroachDB uses the SELECT ... FOR UPDATE locking mechanism during the initial row scan performed in and statement execution. To turn off implicit SELECT ... FOR UPDATE locking for UPDATE and UPSERT statements, set the enable_implicit_select_for_update to false.
For an example, see .

See also