diff --git a/v20.1/selection-queries.md b/v20.1/selection-queries.md index ad600ef9feb..b13c2ef476e 100644 --- a/v20.1/selection-queries.md +++ b/v20.1/selection-queries.md @@ -349,7 +349,7 @@ The general pattern for keyset pagination queries is: {% include copy-clipboard.html %} ~~~ sql -SELECT * FROM t +SELECT * FROM t AS OF SYSTEM TIME ${time} WHERE key > ${value} ORDER BY key LIMIT ${amount} @@ -357,6 +357,10 @@ SELECT * FROM t This is faster than using `LIMIT`/`OFFSET` because, instead of doing a full table scan up to the value of the `OFFSET`, a keyset pagination query looks at a fixed-size set of records for each iteration. This can be done quickly provided that the key used in the `WHERE` clause to implement the pagination is [indexed](indexes.html#best-practices) and [unique](unique.html). A [primary key](primary-key.html) meets both of these criteria. +{{site.data.alerts.callout_info}} +CockroachDB does not have cursors. To support a cursor-like use case, namely "operate on a snapshot of the database at the moment the cursor is opened", use the [`AS OF SYSTEM TIME`](as-of-system-time.html) clause as shown in the examples below. +{{site.data.alerts.end}} + #### Pagination example The examples in this section use the [employees data set](https://github.com/datacharmer/test_db), which you can load into CockroachDB as follows: @@ -368,11 +372,11 @@ USE employees; IMPORT PGDUMP 'https://s3-us-west-1.amazonaws.com/cockroachdb-movr/datasets/employees-db/pg_dump/employees-full.sql.gz'; ~~~ -To get the first page of results using keyset pagination, run: +To get the first page of results using keyset pagination, run the statement below. {% include copy-clipboard.html %} ~~~ sql -SELECT * FROM employees WHERE emp_no > 10000 LIMIT 25; +SELECT * FROM employees AS OF SYSTEM TIME '-1m' WHERE emp_no > 10000 ORDER BY emp_no LIMIT 25; ~~~ ~~~ @@ -389,14 +393,18 @@ Time: 1.31ms ~~~ {{site.data.alerts.callout_success}} -If you don't know what the minimum value of the key is, either `SELECT min(key) FROM table` or use a known minimum value for the key's data type. +When writing your own queries of this type, use a known minimum value for the key's data type. If you don't know what the minimum value of the key is, you can use `SELECT min(key) FROM table`. +{{site.data.alerts.end}} + +{{site.data.alerts.callout_info}} +We use [`AS OF SYSTEM TIME`](as-of-system-time.html) in these examples to ensure that we are operating on a consistent snapshot of the database as of the specified timestamp. This reduces the chance that there will be any concurrent updates to the data the query is accessing, and thus no missing or duplicated rows during the pagination. It also reduces the risk of [transaction retries](transactions.html#client-side-intervention) due to concurrent data access. The value of `-1m` passed to `AS OF SYSTEM TIME` may need to be updated depending on your application's data access patterns. {{site.data.alerts.end}} To get the second page of results, run: {% include copy-clipboard.html %} ~~~ sql -SELECT * FROM employees WHERE emp_no > 10025 LIMIT 25; +SELECT * FROM employees AS OF SYSTEM TIME '-1m' WHERE emp_no > 10025 ORDER BY emp_no LIMIT 25; ~~~ ~~~ @@ -412,11 +420,11 @@ SELECT * FROM employees WHERE emp_no > 10025 LIMIT 25; Time: 1.473ms ~~~ -To get an arbitrary page of results showing employees whose IDs (`emp_no`) are in a much higher range, try the following query. Note that it takes about the same amount of time to run as the previous queries. +To get an arbitrary page of results showing employees whose IDs (`emp_no`) are in a much higher range, run the following query. Note that it takes about the same amount of time to run as the previous queries. {% include copy-clipboard.html %} ~~~ sql -SELECT * FROM employees WHERE emp_no > 300025 LIMIT 25; +SELECT * FROM employees AS OF SYSTEM TIME '-1m' WHERE emp_no > 300025 ORDER BY emp_no LIMIT 25; ~~~ ~~~ @@ -436,7 +444,7 @@ Compare the execution speed of the previous keyset pagination queries with the q {% include copy-clipboard.html %} ~~~ sql -SELECT * FROM employees LIMIT 25 OFFSET 200024; +SELECT * FROM employees AS OF SYSTEM TIME '-1m' LIMIT 25 OFFSET 200024; ~~~ ~~~ @@ -478,7 +486,7 @@ Meanwhile, the keyset pagination queries are looking at a much smaller range of {% include copy-clipboard.html %} ~~~ sql -EXPLAIN SELECT * FROM employees WHERE emp_no > 300025 LIMIT 25; +EXPLAIN SELECT * FROM employees WHERE emp_no > 300025 ORDER BY emp_no LIMIT 25; ~~~ ~~~ @@ -496,10 +504,6 @@ EXPLAIN SELECT * FROM employees WHERE emp_no > 300025 LIMIT 25; Using a sequential (i.e., non-[UUID](uuid.html)) primary key creates hot spots in the database for write-heavy workloads, since concurrent [`INSERT`](insert.html)s to the table will attempt to write to the same (or nearby) underlying [ranges](architecture/overview.html#architecture-range). This can be mitigated by designing your schema with [multi-column primary keys which include a monotonically increasing column](performance-best-practices-overview.html#use-multi-column-primary-keys). {{site.data.alerts.end}} -{{site.data.alerts.callout_info}} -CockroachDB does not implement cursors. For a scale-out system like CockroachDB, using a cursor would not be recommended for the same reason that paginating with `LIMIT`/`OFFSET` is not recommended: it forces the server to keep track of state, which means the pagination queries don't scale well. -{{site.data.alerts.end}} - ## Composability [Selection clauses](#selection-clauses) are defined in the context of selection queries. [Table expressions](table-expressions.html) are defined in the context of the `FROM` sub-clause of [`SELECT`](select-clause.html). Nevertheless, they can be integrated with one another to form more complex queries or statements.