Skip to content

Commit 2a07c18

Browse files
authored
Add docs for CREATE TABLE ... AS OF SYSTEM TIME (#19674)
Fixes DOC-13310 NB. These changes are applied to v25.2, v25.3
1 parent 873cf2b commit 2a07c18

File tree

4 files changed

+184
-2
lines changed

4 files changed

+184
-2
lines changed

src/current/v25.2/create-table-as.md

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ When the results of a query are reused multiple times within a larger query, a v
2222

2323
A view is also advisable when the results must be up-to-date; a view always retrieves the current data from the tables that the view query mentions.
2424

25+
{% include_cached new-in.html version="v25.2" %} Use `CREATE TABLE t AS ...` with the [`AS OF SYSTEM TIME` clause](#populate-create-table-as-with-historical-data-using-as-of-system-time) to leverage [historical reads]({% link {{ page.version.version }}/as-of-system-time.md %}) to reduce contention and improve performance.
26+
2527
{{site.data.alerts.callout_info}}
2628
The default rules for [column families]({% link {{ page.version.version }}/column-families.md %}) apply.
2729
{{site.data.alerts.end}}
@@ -76,7 +78,7 @@ The user must have the `CREATE` [privilege]({% link {{ page.version.version }}/s
7678
`create_as_col_qual_list` | An optional column definition, which may include [primary key constraints]({% link {{ page.version.version }}/primary-key.md %}) and [column family assignments]({% link {{ page.version.version }}/column-families.md %}).
7779
`family_def` | An optional [column family definition]({% link {{ page.version.version }}/column-families.md %}). Column family names must be unique within the table but can have the same name as columns, constraints, or indexes.
7880
`create_as_constraint_def` | An optional [primary key constraint]({% link {{ page.version.version }}/primary-key.md %}).
79-
`select_stmt` | A [selection query]({% link {{ page.version.version }}/selection-queries.md %}) to provide the data.
81+
`select_stmt` | A [selection query]({% link {{ page.version.version }}/select-clause.md %}) to provide the data.
8082
`opt_persistence_temp_table` | Defines the table as a session-scoped temporary table. For more information, see [Temporary Tables]({% link {{ page.version.version }}/temporary-tables.md %}).<br><br>Note that the `LOCAL`, `GLOBAL`, and `UNLOGGED` options are no-ops, allowed by the parser for PostgreSQL compatibility.<br><br>**Support for temporary tables is [in preview]({% link {{ page.version.version }}/cockroachdb-feature-availability.md %}#temporary-objects)**.
8183
`opt_with_storage_parameter_list` | A comma-separated list of [spatial index tuning parameters]({% link {{ page.version.version }}/spatial-indexes.md %}#index-tuning-parameters). Supported parameters include `fillfactor`, `s2_max_level`, `s2_level_mod`, `s2_max_cells`, `geometry_min_x`, `geometry_max_x`, `geometry_min_y`, and `geometry_max_y`. The `fillfactor` parameter is a no-op, allowed for PostgreSQL-compatibility.<br><br>For details, see [Spatial index tuning parameters]({% link {{ page.version.version }}/spatial-indexes.md %}#index-tuning-parameters). For an example, see [Create a spatial index that uses all of the tuning parameters]({% link {{ page.version.version }}/spatial-indexes.md %}#create-a-spatial-index-that-uses-all-of-the-tuning-parameters).
8284
`ON COMMIT PRESERVE ROWS` | This clause is a no-op, allowed by the parser for PostgreSQL compatibility. CockroachDB only supports session-scoped [temporary tables]({% link {{ page.version.version }}/temporary-tables.md %}), and does not support the clauses `ON COMMIT DELETE ROWS` and `ON COMMIT DROP`, which are used to define transaction-scoped temporary tables in PostgreSQL.
@@ -287,6 +289,90 @@ You can define the [column families]({% link {{ page.version.version }}/column-f
287289
(1 row)
288290
~~~
289291

292+
### Populate `CREATE TABLE AS` with historical data using `AS OF SYSTEM TIME`
293+
294+
{% include_cached new-in.html version="v25.2" %} CockroachDB supports creating a table using historical data using the [`AS OF SYSTEM TIME`]({% link {{ page.version.version }}/as-of-system-time.md %}) clause. You can use this to create a new table based on the state of an existing table as of a specific [timestamp]({% link {{ page.version.version }}/timestamp.md %}) in the past. This is useful for:
295+
296+
- Generating static datasets for reporting or analytical workloads without increasing contention on production tables or otherwise impacting performance.
297+
- Analyzing data changes over time.
298+
- Preserving historical data for regulatory or investigative purposes.
299+
- Undoing an [accidental table deletion](#undo-an-accidental-table-deletion).
300+
301+
{{site.data.alerts.callout_info}}
302+
The timestamp must be within the [garbage collection (GC) window]({% link {{ page.version.version }}/architecture/storage-layer.md %}#garbage-collection) of the source table for the data to be available.
303+
{{site.data.alerts.end}}
304+
305+
The following example creates a new table from the [`movr`]({% link {{ page.version.version }}/movr.md %}) dataset at the most recent timestamp that can perform a [follower read]({% link {{ page.version.version }}/follower-reads.md %}).
306+
307+
{% include_cached copy-clipboard.html %}
308+
~~~ sql
309+
CREATE TABLE analysis_vehicle_location_histories
310+
AS SELECT * FROM movr.vehicle_location_histories
311+
AS OF SYSTEM TIME follower_read_timestamp();
312+
~~~
313+
314+
~~~
315+
NOTICE: CREATE TABLE ... AS does not copy over indexes, default expressions, or constraints; the new table has a hidden rowid primary key column
316+
CREATE TABLE AS
317+
~~~
318+
319+
#### Undo an accidental table deletion
320+
321+
The following steps use a table from the [`movr`]({% link {{ page.version.version }}/movr.md %}) dataset to show how to undo an accidental table deletion using `CREATE TABLE AS ... AS OF SYSTEM TIME`.
322+
323+
1. Get a timestamp before the table is deleted in an upcoming step:
324+
325+
{% include_cached copy-clipboard.html %}
326+
~~~ sql
327+
SELECT now();
328+
~~~
329+
330+
~~~
331+
now
332+
--------------------------------
333+
2025-06-17 15:04:15.82632+00
334+
(1 row)
335+
~~~
336+
337+
1. Wait a few seconds to simulate time passing (adjust as needed):
338+
339+
{% include_cached copy-clipboard.html %}
340+
~~~ sql
341+
SELECT pg_sleep(5);
342+
~~~
343+
344+
~~~
345+
pg_sleep
346+
------------
347+
t
348+
(1 row)
349+
~~~
350+
351+
1. Drop the original table:
352+
353+
{% include_cached copy-clipboard.html %}
354+
~~~ sql
355+
DROP TABLE movr.vehicle_location_histories;
356+
~~~
357+
358+
~~~
359+
DROP TABLE
360+
~~~
361+
362+
1. Restore the table using the [`AS OF SYSTEM TIME`]({% link {{ page.version.version }}/as-of-system-time.md %}) clause and the timestamp you obtained before dropping the table:
363+
364+
{% include_cached copy-clipboard.html %}
365+
~~~ sql
366+
CREATE TABLE movr.vehicle_location_histories AS SELECT * FROM movr.vehicle_location_histories AS OF SYSTEM TIME '2025-06-17 15:04:15.82632+00';
367+
~~~
368+
369+
~~~
370+
NOTICE: CREATE TABLE ... AS does not copy over indexes, default expressions, or constraints; the new table has a hidden rowid primary key column
371+
CREATE TABLE AS
372+
~~~
373+
374+
For more information about historical reads at a given timestamp, refer to [`AS OF SYSTEM TIME`]({% link {{ page.version.version }}/as-of-system-time.md %}).
375+
290376
## See also
291377

292378
- [Selection Queries]({% link {{ page.version.version }}/selection-queries.md %})
@@ -299,3 +385,6 @@ You can define the [column families]({% link {{ page.version.version }}/column-f
299385
- [`ALTER PRIMARY KEY`]({% link {{ page.version.version }}/alter-table.md %}#alter-primary-key)
300386
- [`ALTER TABLE`]({% link {{ page.version.version }}/alter-table.md %})
301387
- [Online Schema Changes]({% link {{ page.version.version }}/online-schema-changes.md %})
388+
- [`AS OF SYSTEM TIME`]({% link {{ page.version.version }}/as-of-system-time.md %})
389+
- [Follower Reads]({% link {{ page.version.version }}/follower-reads.md %})
390+
- [Disaster Recovery Planning]({% link {{ page.version.version }}/disaster-recovery-planning.md %})

src/current/v25.2/disaster-recovery-planning.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@ To give yourself more time to recover and clean up the corrupted data, put your
304304

305305
If you are within the [garbage collection window]({% link {{ page.version.version }}/configure-replication-zones.md %}#gc-ttlseconds), run [`AS OF SYSTEM TIME`]({% link {{ page.version.version }}/as-of-system-time.md %}) queries and use [`CREATE TABLE AS … SELECT * FROM`]({% link {{ page.version.version }}/create-table-as.md %}) to create comparison data and run differentials to find the offending rows to fix.
306306

307+
{% include_cached new-in.html version="v25.2" %} Alternatively, you can use [`CREATE TABLE AS ... AS OF SYSTEM TIME`]({% link {{ page.version.version }}/create-table-as.md %}#populate-create-table-as-with-historical-data-using-as-of-system-time) to generate a copy of the data as of a specific [timestamp]({% link {{ page.version.version }}/timestamp.md %}). This also requires being within the garbage collection window.
308+
307309
If you are outside of the garbage collection window, you will need to use a [backup]({% link {{ page.version.version }}/backup.md %}) to run comparisons.
308310

309311
### Restore to a point in time

src/current/v25.3/create-table-as.md

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ When the results of a query are reused multiple times within a larger query, a v
2222

2323
A view is also advisable when the results must be up-to-date; a view always retrieves the current data from the tables that the view query mentions.
2424

25+
Use `CREATE TABLE t AS ...` with the [`AS OF SYSTEM TIME` clause](#populate-create-table-as-with-historical-data-using-as-of-system-time) to leverage [historical reads]({% link {{ page.version.version }}/as-of-system-time.md %}) to reduce contention and improve performance.
26+
2527
{{site.data.alerts.callout_info}}
2628
The default rules for [column families]({% link {{ page.version.version }}/column-families.md %}) apply.
2729
{{site.data.alerts.end}}
@@ -76,7 +78,7 @@ The user must have the `CREATE` [privilege]({% link {{ page.version.version }}/s
7678
`create_as_col_qual_list` | An optional column definition, which may include [primary key constraints]({% link {{ page.version.version }}/primary-key.md %}) and [column family assignments]({% link {{ page.version.version }}/column-families.md %}).
7779
`family_def` | An optional [column family definition]({% link {{ page.version.version }}/column-families.md %}). Column family names must be unique within the table but can have the same name as columns, constraints, or indexes.
7880
`create_as_constraint_def` | An optional [primary key constraint]({% link {{ page.version.version }}/primary-key.md %}).
79-
`select_stmt` | A [selection query]({% link {{ page.version.version }}/selection-queries.md %}) to provide the data.
81+
`select_stmt` | A [selection query]({% link {{ page.version.version }}/select-clause.md %}) to provide the data.
8082
`opt_persistence_temp_table` | Defines the table as a session-scoped temporary table. For more information, see [Temporary Tables]({% link {{ page.version.version }}/temporary-tables.md %}).<br><br>Note that the `LOCAL`, `GLOBAL`, and `UNLOGGED` options are no-ops, allowed by the parser for PostgreSQL compatibility.<br><br>**Support for temporary tables is [in preview]({% link {{ page.version.version }}/cockroachdb-feature-availability.md %}#temporary-objects)**.
8183
`opt_with_storage_parameter_list` | A comma-separated list of [spatial index tuning parameters]({% link {{ page.version.version }}/spatial-indexes.md %}#index-tuning-parameters). Supported parameters include `fillfactor`, `s2_max_level`, `s2_level_mod`, `s2_max_cells`, `geometry_min_x`, `geometry_max_x`, `geometry_min_y`, and `geometry_max_y`. The `fillfactor` parameter is a no-op, allowed for PostgreSQL-compatibility.<br><br>For details, see [Spatial index tuning parameters]({% link {{ page.version.version }}/spatial-indexes.md %}#index-tuning-parameters). For an example, see [Create a spatial index that uses all of the tuning parameters]({% link {{ page.version.version }}/spatial-indexes.md %}#create-a-spatial-index-that-uses-all-of-the-tuning-parameters).
8284
`ON COMMIT PRESERVE ROWS` | This clause is a no-op, allowed by the parser for PostgreSQL compatibility. CockroachDB only supports session-scoped [temporary tables]({% link {{ page.version.version }}/temporary-tables.md %}), and does not support the clauses `ON COMMIT DELETE ROWS` and `ON COMMIT DROP`, which are used to define transaction-scoped temporary tables in PostgreSQL.
@@ -287,6 +289,90 @@ You can define the [column families]({% link {{ page.version.version }}/column-f
287289
(1 row)
288290
~~~
289291

292+
### Populate `CREATE TABLE AS` with historical data using `AS OF SYSTEM TIME`
293+
294+
CockroachDB supports creating a table using historical data using the [`AS OF SYSTEM TIME`]({% link {{ page.version.version }}/as-of-system-time.md %}) clause. You can use this to create a new table based on the state of an existing table as of a specific [timestamp]({% link {{ page.version.version }}/timestamp.md %}) in the past. This is useful for:
295+
296+
- Generating static datasets for reporting or analytical workloads without increasing contention on production tables or otherwise impacting performance.
297+
- Analyzing data changes over time.
298+
- Preserving historical data for regulatory or investigative purposes.
299+
- Undoing an [accidental table deletion](#undo-an-accidental-table-deletion).
300+
301+
{{site.data.alerts.callout_info}}
302+
The timestamp must be within the [garbage collection (GC) window]({% link {{ page.version.version }}/architecture/storage-layer.md %}#garbage-collection) of the source table for the data to be available.
303+
{{site.data.alerts.end}}
304+
305+
The following example creates a new table from the [`movr`]({% link {{ page.version.version }}/movr.md %}) dataset at the most recent timestamp that can perform a [follower read]({% link {{ page.version.version }}/follower-reads.md %}).
306+
307+
{% include_cached copy-clipboard.html %}
308+
~~~ sql
309+
CREATE TABLE analysis_vehicle_location_histories
310+
AS SELECT * FROM movr.vehicle_location_histories
311+
AS OF SYSTEM TIME follower_read_timestamp();
312+
~~~
313+
314+
~~~
315+
NOTICE: CREATE TABLE ... AS does not copy over indexes, default expressions, or constraints; the new table has a hidden rowid primary key column
316+
CREATE TABLE AS
317+
~~~
318+
319+
#### Undo an accidental table deletion
320+
321+
The following steps use a table from the [`movr`]({% link {{ page.version.version }}/movr.md %}) dataset to show how to undo an accidental table deletion using `CREATE TABLE AS ... AS OF SYSTEM TIME`.
322+
323+
1. Get a timestamp before the table is deleted in an upcoming step:
324+
325+
{% include_cached copy-clipboard.html %}
326+
~~~ sql
327+
SELECT now();
328+
~~~
329+
330+
~~~
331+
now
332+
--------------------------------
333+
2025-06-17 15:04:15.82632+00
334+
(1 row)
335+
~~~
336+
337+
1. Wait a few seconds to simulate time passing (adjust as needed):
338+
339+
{% include_cached copy-clipboard.html %}
340+
~~~ sql
341+
SELECT pg_sleep(5);
342+
~~~
343+
344+
~~~
345+
pg_sleep
346+
------------
347+
t
348+
(1 row)
349+
~~~
350+
351+
1. Drop the original table:
352+
353+
{% include_cached copy-clipboard.html %}
354+
~~~ sql
355+
DROP TABLE movr.vehicle_location_histories;
356+
~~~
357+
358+
~~~
359+
DROP TABLE
360+
~~~
361+
362+
1. Restore the table using the [`AS OF SYSTEM TIME`]({% link {{ page.version.version }}/as-of-system-time.md %}) clause and the timestamp you obtained before dropping the table:
363+
364+
{% include_cached copy-clipboard.html %}
365+
~~~ sql
366+
CREATE TABLE movr.vehicle_location_histories AS SELECT * FROM movr.vehicle_location_histories AS OF SYSTEM TIME '2025-06-17 15:04:15.82632+00';
367+
~~~
368+
369+
~~~
370+
NOTICE: CREATE TABLE ... AS does not copy over indexes, default expressions, or constraints; the new table has a hidden rowid primary key column
371+
CREATE TABLE AS
372+
~~~
373+
374+
For more information about historical reads at a given timestamp, refer to [`AS OF SYSTEM TIME`]({% link {{ page.version.version }}/as-of-system-time.md %}).
375+
290376
## See also
291377

292378
- [Selection Queries]({% link {{ page.version.version }}/selection-queries.md %})
@@ -299,3 +385,6 @@ You can define the [column families]({% link {{ page.version.version }}/column-f
299385
- [`ALTER PRIMARY KEY`]({% link {{ page.version.version }}/alter-table.md %}#alter-primary-key)
300386
- [`ALTER TABLE`]({% link {{ page.version.version }}/alter-table.md %})
301387
- [Online Schema Changes]({% link {{ page.version.version }}/online-schema-changes.md %})
388+
- [`AS OF SYSTEM TIME`]({% link {{ page.version.version }}/as-of-system-time.md %})
389+
- [Follower Reads]({% link {{ page.version.version }}/follower-reads.md %})
390+
- [Disaster Recovery Planning]({% link {{ page.version.version }}/disaster-recovery-planning.md %})

src/current/v25.3/disaster-recovery-planning.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@ To give yourself more time to recover and clean up the corrupted data, put your
304304

305305
If you are within the [garbage collection window]({% link {{ page.version.version }}/configure-replication-zones.md %}#gc-ttlseconds), run [`AS OF SYSTEM TIME`]({% link {{ page.version.version }}/as-of-system-time.md %}) queries and use [`CREATE TABLE AS … SELECT * FROM`]({% link {{ page.version.version }}/create-table-as.md %}) to create comparison data and run differentials to find the offending rows to fix.
306306

307+
Alternatively, you can use [`CREATE TABLE AS ... AS OF SYSTEM TIME`]({% link {{ page.version.version }}/create-table-as.md %}#populate-create-table-as-with-historical-data-using-as-of-system-time) to generate a copy of the data as of a specific [timestamp]({% link {{ page.version.version }}/timestamp.md %}). This also requires being within the garbage collection window.
308+
307309
If you are outside of the garbage collection window, you will need to use a [backup]({% link {{ page.version.version }}/backup.md %}) to run comparisons.
308310

309311
### Restore to a point in time

0 commit comments

Comments
 (0)