A view is a stored SELECT query represented as a virtual table. Unlike a standard table, a view is not part of the physical schema; instead, it is a virtual table that forms dynamically when requested.

Why Use Views?

There are various reasons to use views, including:

Hide query complexity

When you have a complex query that, for example, joins several tables, or performs complex calculations, you can store the query as a view and then select from the view as you would from a standard table.

Example

Let’s say you’re using our sample startrek database, which contains two tables, episodes and quotes. There’s 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:

> 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(*) |
+--------+----------+
|      2 |       76 |
|      3 |       46 |
|      1 |       78 |
+--------+----------+
(3 rows)

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

> 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;
CREATE VIEW

Then, executing the query is as easy as SELECTing from the view:

> SELECT * FROM startrek.quotes_per_season;
+--------+--------+
| season | quotes |
+--------+--------+
|      2 |     76 |
|      3 |     46 |
|      1 |     78 |
+--------+--------+
(3 rows)

Limit access to underlying data

When you do not want to grant a user access to all the data in one or more standard tables, you can create a view that contains only the columns and/or rows that the user should have access to and then grant the user permissions on the view.

Example

Let’s say you have a bank database containing an accounts table:

> SELECT * FROM bank.accounts;
+----+----------+---------+-----------------+
| id |   type   | balance |      email      |
+----+----------+---------+-----------------+
|  1 | checking |    1000 | [email protected]   |
|  2 | savings  |   10000 | [email protected]   |
|  3 | checking |   15000 | [email protected] |
|  4 | checking |    5000 | [email protected] |
|  5 | savings  |   50000 | [email protected]   |
+----+----------+---------+-----------------+
(5 rows)

You want a particular user, bob, to be able to see the types of accounts each user has without seeing the balance in each account, so you create a view to expose just the type and email columns:

> CREATE VIEW bank.user_accounts
  AS SELECT type, email
  FROM bank.accounts;
CREATE VIEW

You then make sure bob does not have privileges on the underlying bank.accounts table:

> SHOW GRANTS ON bank.accounts;
+----------+------+------------+
|  Table   | User | Privileges |
+----------+------+------------+
| accounts | root | ALL        |
| accounts | toti | SELECT     |
+----------+------+------------+
(2 rows)

Finally, you grant bob privileges on the bank.user_accounts view:

> GRANT SELECT ON bank.user_accounts TO bob;

Now, bob will get a permissions error when trying to access the underlying bank.accounts table but will be allowed to query the bank.user_accounts view:

> SELECT * FROM bank.accounts;
pq: user bob does not have SELECT privilege on table accounts
> SELECT * FROM bank.user_accounts;
+----------+-----------------+
|   type   |      email      |
+----------+-----------------+
| checking | [email protected]   |
| savings  | [email protected]   |
| checking | [email protected] |
| checking | [email protected] |
| savings  | [email protected]   |
+----------+-----------------+
(5 rows)

Simplify supporting legacy code

When you need to alter a table in a way that will break your application, and you can’t update your code right away, you can create a view with a name and schema identical to the original table. During and after changes to the original table, your application will continue uninterrupted, and you can update the legacy code at your leisure.

Example

Let’s say you have a table called user_accounts that you want to rename to client_accounts, but your application has many queries to user_accounts. To ensure that your application continues uninterrupted, you could execute a transaction that renames the underlying table to client_accounts and creates a view with the old table name, user_accounts:

BEGIN;
ALTER TABLE bank.user_accounts RENAME TO bank.client_accounts;
CREATE VIEW bank.user_accounts
  AS SELECT type, email
  FROM bank.client_accounts;
COMMIT;

Your application would then continue referencing user_accounts without issue, and at a later time, you could update your application code to reference the new client_accounts table.

How Views Work

Creating Views

To create a view, use the CREATE VIEW statement:

> CREATE VIEW bank.user_accounts
  AS SELECT type, email
  FROM bank.accounts;
CREATE VIEW

Listing Views

Once created, views are represented as virtual tables alongside other virtual and standard tables in the database:

> SHOW TABLES FROM bank;
+---------------+
|     Table     |
+---------------+
| accounts      |
| user_accounts |
+---------------+
(2 rows)

To list just views, you can query the views table in the built-in information_schema database:

> SELECT * FROM information_schema.views;
+---------------+-------------------+----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+----------------------------+
| TABLE_CATALOG |   TABLE_SCHEMA    |      TABLE_NAME      |                                                                              VIEW_DEFINITION                                                                              | CHECK_OPTION | IS_UPDATABLE | IS_INSERTABLE_INTO | IS_TRIGGER_UPDATABLE | IS_TRIGGER_DELETABLE | IS_TRIGGER_INSERTABLE_INTO |
+---------------+-------------------+----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+----------------------------+
| def           | bank              | user_accounts        | SELECT type, email FROM bank.accounts                                                                                                                                     | NULL         | NULL         | NULL               | NULL                 | NULL                 | NULL                       |
| def           | startrek          | quotes_per_season    | SELECT startrek.episodes.season, count(*) FROM startrek.quotes JOIN startrek.episodes ON startrek.quotes.episode = startrek.episodes.id GROUP BY startrek.episodes.season | NULL         | NULL         | NULL               | NULL                 | NULL                 | NULL                       |
+---------------+-------------------+----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+----------------------------+
(2 rows)

Alternatively, you can query the pg_views table in the built-in pg_catalog database:

> SELECT * FROM pg_catalog.pg_views;
+-------------------+----------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|    schemaname     |       viewname       | viewowner |                                                                                definition                                                                                 |
+-------------------+----------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| bank              | user_accounts        | NULL      | SELECT type, email FROM bank.accounts                                                                                                                                     |
| startrek          | quotes_per_season    | NULL      | SELECT startrek.episodes.season, count(*) FROM startrek.quotes JOIN startrek.episodes ON startrek.quotes.episode = startrek.episodes.id GROUP BY startrek.episodes.season |
+-------------------+----------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
(2 rows)

Querying Views

To query a view, target it with a SELECT statement just as you would a standard table:

> SELECT * FROM bank.user_accounts;
+----------+-----------------+
|   type   |      email      |
+----------+-----------------+
| checking | [email protected]   |
| savings  | [email protected]   |
| checking | [email protected] |
| checking | [email protected] |
| savings  | [email protected]   |
+----------+-----------------+
(5 rows)

SELECTing a view executes the view’s stored SELECT statement, which returns the relevant data from the underlying table(s). To inspect the SELECT statement executed by the view, use the SHOW CREATE VIEW statement:

> SHOW CREATE VIEW bank.user_accounts;
+--------------------+---------------------------------------------------------------------------+
|        View        |                                CreateView                                 |
+--------------------+---------------------------------------------------------------------------+
| bank.user_accounts | CREATE VIEW "bank.user_accounts" AS SELECT type, email FROM bank.accounts |
+--------------------+---------------------------------------------------------------------------+
(1 row)

You can also inspect the SELECT statement executed by a view by querying the views table in the built-in information_schema database:

> SELECT * FROM information_schema.views;
+---------------+-------------------+----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+----------------------------+
| TABLE_CATALOG |   TABLE_SCHEMA    |      TABLE_NAME      |                                                                              VIEW_DEFINITION                                                                              | CHECK_OPTION | IS_UPDATABLE | IS_INSERTABLE_INTO | IS_TRIGGER_UPDATABLE | IS_TRIGGER_DELETABLE | IS_TRIGGER_INSERTABLE_INTO |
+---------------+-------------------+----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+----------------------------+
| def           | bank              | user_accounts        | SELECT type, email FROM bank.accounts                                                                                                                                     | NULL         | NULL         | NULL               | NULL                 | NULL                 | NULL                       |
| def           | startrek          | quotes_per_season    | SELECT startrek.episodes.season, count(*) FROM startrek.quotes JOIN startrek.episodes ON startrek.quotes.episode = startrek.episodes.id GROUP BY startrek.episodes.season | NULL         | NULL         | NULL               | NULL                 | NULL                 | NULL                       |
+---------------+-------------------+----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+--------------+--------------------+----------------------+----------------------+----------------------------+
(2 rows)

View Dependencies

A view depends on the objects targeted by its SELECT statement. Attempting to rename an object referenced in a view’s SELECT statement therefore results in an error:

> ALTER TABLE bank.accounts RENAME TO bank.accts;
pq: cannot rename table "bank.accounts" because view "user_accounts" depends on it

Likewise, attempting to drop an object referenced in a view’s SELECT statement results in an error:

> DROP TABLE bank.accounts;
pq: cannot drop table "accounts" because view "user_accounts" depends on it
> ALTER TABLE bank.accounts DROP COLUMN email;
pq: cannot drop column email because view "bank.user_accounts" depends on it

There is an exception to the rule above, however: When dropping a table or dropping a view, you can use the CASCADE keyword to drop all dependent objects as well:

> DROP TABLE bank.accounts CASCADE;
DROP TABLE
CASCADE drops all dependent objects without listing them, which can lead to inadvertent and difficult-to-recover losses. To avoid potential harm, we recommend dropping objects individually in most cases.

Renaming Views

To rename a view, use the ALTER VIEW statement:

> ALTER VIEW bank.user_accounts RENAME TO bank.user_accts;
RENAME VIEW

It is not possible to change the SELECT statement executed by the view. Instead, you must drop the existing view and create a new view.

Removing Views

To remove a view, use the DROP VIEW statement:

> DROP VIEW bank.user_accounts
DROP VIEW

See Also



Yes No