> ## Documentation Index
> Fetch the complete documentation index at: https://cockroachlabs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# CREATE VIEW

export const InternalLink = ({version, path = "", children, ...props}) => {
  let detectedVersion = version || "stable";
  if (typeof window !== 'undefined' && !version) {
    const match = window.location.pathname.match(/\/docs\/([^/]+)/);
    if (match) {
      detectedVersion = match[1];
    }
  }
  const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
  return <a href={`/docs/${detectedVersion}/${normalizedPath}`} {...props}>
      {children}
    </a>;
};

The `CREATE VIEW` statement creates a new <InternalLink path="views">view</InternalLink>, which is a stored query represented as a virtual table.

<Note>
  By default, views created in a database cannot reference objects in a different database. To enable cross-database references for views, set the `sql.cross_db_views.enabled` <InternalLink path="cluster-settings">cluster setting</InternalLink> to `true`.
</Note>

<Note>
  The \`\` statement performs a schema change. For more information about how online schema changes work in CockroachDB, see <InternalLink path="online-schema-changes">Online Schema Changes</InternalLink>.
</Note>

## Required privileges

The user must have the `CREATE` <InternalLink path="security-reference/authorization#managing-privileges">privilege</InternalLink> on the parent database and the `SELECT` privilege on any table(s) referenced by the view.

## Synopsis

<img src="https://mintcdn.com/cockroachlabs/Nqtj0HvOrM_ugxgN/images/sql-diagrams/v26.2/create_view.svg?fit=max&auto=format&n=Nqtj0HvOrM_ugxgN&q=85&s=3ad963b1818db72e2a81846e1670e9d6" alt="create_view syntax diagram" style={{maxWidth: "100%", overflowX: "auto"}} width="1195" height="255" data-path="images/sql-diagrams/v26.2/create_view.svg" />

## Parameters

| Parameter                        | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `MATERIALIZED`                   | Create a <InternalLink path="views#materialized-views">materialized view</InternalLink>.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `IF NOT EXISTS`                  | Create a new view only if a view of the same name does not already exist. If one does exist, do not return an error.<br /><br />Note that `IF NOT EXISTS` checks the view name only. It does not check if an existing view has the same columns as the new view.                                                                                                                                                                                                                                                                                                                                                                           |
| `OR REPLACE`                     | Create a new view if a view of the same name does not already exist. If a view of the same name already exists, replace that view.<br /><br />In order to replace an existing view, the new view must have the same columns as the existing view, or more. If the new view has additional columns, the old columns must be a prefix of the new columns. For example, if the existing view has columns `a, b`, the new view can have an additional column `c`, but must have columns `a, b` as a prefix. In this case, `CREATE OR REPLACE VIEW myview (a, b, c)` would be allowed, but `CREATE OR REPLACE VIEW myview (b, a, c)` would not. |
| `view_name`                      | The name of the view to create, which must be unique within its database and follow these <InternalLink path="keywords-and-identifiers#identifiers">identifier rules</InternalLink>. When the parent database is not set as the default, the name must be formatted as `database.name`.                                                                                                                                                                                                                                                                                                                                                    |
| `name_list`                      | An optional, comma-separated list of column names for the view. If specified, these names will be used in the response instead of the columns specified in `AS select_stmt`.                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `WITH (security_invoker = true)` | Sets whether to check privileges on the underlying tables as the querying user (`true`) or the view owner (`false`). `WITH (security_invoker)` is equivalent to `WITH (security_invoker = true)`. This option applies only to non-materialized views.                                                                                                                                                                                                                                                                                                                                                                                      |
| `AS select_stmt`                 | The <InternalLink path="selection-queries">selection query</InternalLink> to execute when the view is requested.<br /><br />Note that it is not currently possible to use `*` to select all columns from a referenced table or view; instead, you must specify specific columns.                                                                                                                                                                                                                                                                                                                                                           |
| `AS OF SYSTEM TIME`              | When used with `CREATE MATERIALIZED VIEW`, populates the materialized view using historical data. This can reduce <InternalLink path="performance-best-practices-overview#transaction-contention">contention</InternalLink> by leveraging <InternalLink path="follower-reads">follower reads</InternalLink>. The timestamp must be within the <InternalLink path="configure-replication-zones">garbage collection window</InternalLink>. For more information, see <InternalLink path="as-of-system-time">`AS OF SYSTEM TIME`</InternalLink>.                                                                                              |
| `opt_temp`                       | Defines the view as a session-scoped temporary view. For more information, see <InternalLink path="views#temporary-views">Temporary Views</InternalLink>.<br /><br />**Support for temporary views is <InternalLink path="cockroachdb-feature-availability">in preview</InternalLink>**.                                                                                                                                                                                                                                                                                                                                                   |

## Example

<Tip>
  This example highlights one key benefit to using views: simplifying complex queries. For additional benefits and examples, see <InternalLink path="views">Views</InternalLink>.
</Tip>

### Setup

The following examples use the <InternalLink path="cockroach-demo#datasets">`startrek` demo database schema</InternalLink>.

To follow along, run <InternalLink path="cockroach-demo">`cockroach demo startrek`</InternalLink> to start a temporary, in-memory cluster with the `startrek` schema and dataset preloaded:

```shell theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
$ cockroach demo startrek
```

### Create a view

The sample `startrek` database contains two tables, `episodes` and `quotes`. The table also contains a foreign key constraint, between the `episodes.id` column and the `quotes.episode` column. To count the number of famous quotes per season, you could run the following join:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
> SELECT startrek.episodes.season, count(*)
  FROM startrek.quotes
  JOIN startrek.episodes
  ON startrek.quotes.episode = startrek.episodes.id
  GROUP BY startrek.episodes.season;
```

```
  season | count
---------+--------
       1 |    78
       2 |    76
       3 |    46
(3 rows)
```

Alternatively, to make it much easier to run this complex query, you could create a view:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
> CREATE VIEW startrek.quotes_per_season (season, quotes)
  AS SELECT startrek.episodes.season, count(*)
  FROM startrek.quotes
  JOIN startrek.episodes
  ON startrek.quotes.episode = startrek.episodes.id
  GROUP BY startrek.episodes.season;
```

The view is then represented as a virtual table alongside other tables in the database:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
> SHOW TABLES FROM startrek;
```

```
  schema_name |    table_name     | type  | estimated_row_count
--------------+-------------------+-------+----------------------
  public      | episodes          | table |                  79
  public      | quotes            | table |                 200
  public      | quotes_per_season | view  |                   3
(3 rows)
```

Executing the query is as easy as `SELECT`ing from the view, as you would from a standard table:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
> SELECT * FROM startrek.quotes_per_season;
```

```
  season | quotes
---------+---------
       3 |     46
       1 |     78
       2 |     76
(3 rows)
```

### Replace an existing view

You can create a new view, or replace an existing view, with `CREATE OR REPLACE VIEW`:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
> CREATE OR REPLACE VIEW startrek.quotes_per_season (season, quotes)
  AS SELECT startrek.episodes.season, count(*)
  FROM startrek.quotes
  JOIN startrek.episodes
  ON startrek.quotes.episode = startrek.episodes.id
  GROUP BY startrek.episodes.season
  ORDER BY startrek.episodes.season;
```

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
> SELECT * FROM startrek.quotes_per_season;
```

```
  season | quotes
---------+---------
       3 |     46
       1 |     78
       2 |     76
(3 rows)
```

### Create a view with invoker privileges

To make a view check privileges on the underlying tables as the querying user, include the <InternalLink path="views#use-invoker-privileges-for-underlying-data">`security_invoker` option</InternalLink>:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
CREATE VIEW user_accounts WITH (security_invoker) AS
  SELECT type, email
  FROM accounts;
```

To restore the default behavior later, use <InternalLink path="alter-view">`ALTER VIEW ... RESET (security_invoker)`</InternalLink>.

### Create a view that references routines

Views can call both scalar and set-returning <InternalLink path="user-defined-functions">user-defined functions (UDFs)</InternalLink> in their `SELECT` statements.

The following example builds a view over a table and two UDFs.

Create and populate a table:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
CREATE TABLE xy (x INT, y INT);
INSERT INTO xy VALUES (1, 2), (3, 4), (5, 6);
```

Define a scalar and a set-returning UDF:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
CREATE FUNCTION f_scalar() RETURNS INT LANGUAGE SQL AS $$
    SELECT count(*) FROM xy;
  $$;
```

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
CREATE FUNCTION f_setof() RETURNS SETOF xy LANGUAGE SQL AS $$
    SELECT * FROM xy;
  $$;
```

Create a view that references both functions:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
CREATE VIEW v_xy AS
SELECT x, y, f_scalar() AS total_rows
FROM f_setof();
```

Query the view:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
SELECT * FROM v_xy ORDER BY x;
```

```
  x | y | total_rows
----+---+-------------
  1 | 2 |          3
  3 | 4 |          3
  5 | 6 |          3
(3 rows)
```

Because the view depends on `f_scalar` and `f_setof`, attempting to rename either function returns an error:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
ALTER FUNCTION f_scalar RENAME TO f_scalar_renamed;
```

```
ERROR: cannot rename function "f_scalar" because other functions or views ([movr.public.v_xy]) still depend on it
SQLSTATE: 0A000
```

### Create a materialized view with historical data using `AS OF SYSTEM TIME`

You can create a materialized view using historical data with the <InternalLink path="as-of-system-time">`AS OF SYSTEM TIME`</InternalLink> clause. This is useful for reducing <InternalLink path="performance-best-practices-overview#transaction-contention">contention</InternalLink> by performing a <InternalLink path="follower-reads">follower read</InternalLink> when populating the view.

<Note>
  Historical data is available only within the <InternalLink path="configure-replication-zones">garbage collection window</InternalLink>.
</Note>

The following example creates a materialized view using the most recent data that is available for <InternalLink path="follower-reads">follower reads</InternalLink>:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
CREATE MATERIALIZED VIEW overdrawn_accounts
  AS SELECT id, balance
  FROM bank
  WHERE balance < 0
  AS OF SYSTEM TIME follower_read_timestamp();
```

You can also specify an explicit timestamp:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
CREATE MATERIALIZED VIEW overdrawn_accounts
  AS SELECT id, balance
  FROM bank
  WHERE balance < 0
  AS OF SYSTEM TIME '-10s';
```

## See also

* <InternalLink path="selection-queries">Selection Queries</InternalLink>
* <InternalLink path="views">Views</InternalLink>
* <InternalLink path="show-create">`SHOW CREATE`</InternalLink>
* <InternalLink path="alter-view">`ALTER VIEW`</InternalLink>
* <InternalLink path="drop-view">`DROP VIEW`</InternalLink>
* <InternalLink path="online-schema-changes">Online Schema Changes</InternalLink>
* <InternalLink path="as-of-system-time">`AS OF SYSTEM TIME`</InternalLink>
* <InternalLink path="follower-reads">Follower Reads</InternalLink>
