mirror of
https://github.com/supabase/supabase.git
synced 2026-10-08 02:45:07 +03:00
Part 2 of a 5-PR stack on `apps/docs/content/guides/database/tables.mdx`. Builds on #50021. ## Problem Four structural problems, all covered by the Guides section of CONTRIBUTING. **Views was a second topic living inside a guide about tables.** Roughly 180 lines, its own subsections four levels deep, sharing nothing with the tables above it beyond the word "table". **The page didn't say what it was for.** It opened with three paragraphs and a sample table before a reader could tell whether the page matched their goal. CONTRIBUTING asks a guide to begin with a sentence declaring its intent. **The top level mixed information types.** It was a flat list of every task, so "Schemas" and "Primary keys" sat beside "Creating tables" and background interrupted the action path. **Reference material interrupted the procedure.** A 44-row data type table sat between "Creating tables" and "Loading data", so a reader following the action path walked through it. Plus a duplicate video: the Dashboard tab under "Joining tables with foreign keys" embedded the same YouTube ID that frontmatter already serves as the table of contents video. ## Solution Moves and regrouping. - **Views moves to its own page**, `guides/database/views`, with its headings promoted one level and the two view-related links from Resources moved with it. - The page opens with an intent sentence, then a section outline, then a "What is a table?" section holding the definition and the spreadsheet comparison. - The remaining sections split into three groups by information type, ordered procedures, context, reference: **Creating and managing tables** holds creating, loading, and joining; **How tables are organized** holds primary keys, relationships, and schemas; **Reference** holds the data type table. - "Joining tables with foreign keys" held both classes, so it splits. The steps keep the heading and stay in the procedures group. The concept, what relational means and the diagram showing it, becomes **Relationships between tables** in the context group. The two cross-reference each other. - The duplicate video goes, and with the Dashboard tab empty the surrounding `Tabs` wrapper goes too. ## Anchors **Every heading keeps its text, so every anchor keeps its slug.** Demoting a heading changes its level, not its anchor. That matters because the inbound links are mostly outside `apps/docs`: Studio deep-links to `#data-types` from three components and `#primary-keys` from two, and `apps/www` links to `#creating-tables` and `#joining-tables-with-foreign-keys`. `#views` is the one exception, since that content left the page. Its single inbound link, in `guides/ai/engineering-for-scale.mdx`, now points at the new page, and both `NavigationMenu.constants.ts` entries are updated: the existing item becomes "Managing tables and data" and a "Views" item sits beside it. ## One deletion that isn't a move The "Columns" heading and its one sentence, "You must define the data type when you create a column." The heading held only the two subsections that moved out, and the sentence repeats a line 50 lines above it. ## Deferred Reordering "View security" behind an access-control foundation. That move only reads correctly once the foundation exists, so it travels with that content in #50024. ## Manual testing Preview: https://docs-git-docs-tables-structure-supabase.vercel.app/docs/guides/database/tables 1. Open the preview. The page opens with its intent, then a four-entry outline, then "What is a table?". Each outline link resolves, and the three groups below read as procedures, then context, then reference. 2. Open `#data-types`, `#primary-keys`, `#creating-tables`, and `#joining-tables-with-foreign-keys` on the preview. All four still land on their sections. 3. Open https://docs-git-docs-tables-structure-supabase.vercel.app/docs/guides/database/views. The new page renders, and "Views" appears in the sidebar beside "Managing tables and data". 4. Run `pnpm build:guides-markdown` from `apps/docs`. It generates 782 files, one more than before. Discard the change to `public/markdown/manifest.json`, which the repo commits as `[]`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Added a dedicated guide covering Postgres views, including creation, querying, security options, and materialized views. - Reorganized the Tables and data guide with clearer sections, navigation links, table organization details, and reference information. - Updated the many-to-many example to display SQL directly. - Split database navigation into separate “Managing tables and data” and “Views” entries. - Added a PostgreSQL log configuration entry and a C# client reference link. - Updated documentation links to point to the new Views guide. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
187 lines
6.3 KiB
Plaintext
187 lines
6.3 KiB
Plaintext
---
|
|
id: 'views'
|
|
title: 'Views'
|
|
description: 'Creating and using views in Postgres.'
|
|
---
|
|
|
|
Learn what views are and when to use them.
|
|
|
|
Views sit on top of the tables you create in [Tables and data](/docs/guides/database/tables).
|
|
|
|
A view is a convenient shortcut to a query. Creating a view doesn't involve new tables or data. When you run a view, Postgres executes the underlying query and returns its results.
|
|
|
|
Say you have the following tables from a university database:
|
|
|
|
**`students`**
|
|
|
|
{/* supa-mdx-lint-disable Rule003Spelling */}
|
|
|
|
| id | name | type |
|
|
| --- | ---------------- | ------------- |
|
|
| 1 | Princess Leia | undergraduate |
|
|
| 2 | Yoda | graduate |
|
|
| 3 | Anakin Skywalker | graduate |
|
|
|
|
{/* supa-mdx-lint-enable Rule003Spelling */}
|
|
|
|
**`courses`**
|
|
|
|
| id | title | code |
|
|
| --- | ------------------------ | ------- |
|
|
| 1 | Introduction to Postgres | PG101 |
|
|
| 2 | Authentication Theories | AUTH205 |
|
|
| 3 | Fundamentals of Supabase | SUP412 |
|
|
|
|
**`grades`**
|
|
|
|
| id | student_id | course_id | result |
|
|
| --- | ---------- | --------- | ------ |
|
|
| 1 | 1 | 1 | B+ |
|
|
| 2 | 1 | 3 | A+ |
|
|
| 3 | 2 | 2 | A |
|
|
| 4 | 3 | 1 | A- |
|
|
| 5 | 3 | 2 | A |
|
|
| 6 | 3 | 3 | B- |
|
|
|
|
Creating a view that consists of all three tables looks like this:
|
|
|
|
```sql
|
|
create view transcripts as
|
|
select
|
|
students.name,
|
|
students.type,
|
|
courses.title,
|
|
courses.code,
|
|
grades.result
|
|
from grades
|
|
left join students on grades.student_id = students.id
|
|
left join courses on grades.course_id = courses.id;
|
|
|
|
grant all on table transcripts to authenticated;
|
|
```
|
|
|
|
Then you can access the underlying query with:
|
|
|
|
```sql
|
|
select * from transcripts;
|
|
```
|
|
|
|
## View security
|
|
|
|
By default, Postgres checks permissions on a view's underlying tables against the view's owner rather than the role running the query. If a privileged role creates a view, everyone reading it does so through that role's permissions. Define the view with the `security_invoker` modifier to check the querying role's permissions instead, which is also what makes the underlying tables' row level security policies apply.
|
|
|
|
```sql
|
|
-- switch an existing view to the querying role's permissions
|
|
alter view <view name>
|
|
set (security_invoker = true);
|
|
|
|
-- create a view with the security_invoker modifier
|
|
create view <view name> with(security_invoker=true) as (
|
|
select * from <some table>
|
|
);
|
|
```
|
|
|
|
## When to use views
|
|
|
|
Views provide several benefits.
|
|
|
|
### Simplicity
|
|
|
|
As a query becomes more complex, calling it repeatedly gets tedious, especially when you run it regularly. In the example above, instead of repeatedly running:
|
|
|
|
```sql
|
|
select
|
|
students.name,
|
|
students.type,
|
|
courses.title,
|
|
courses.code,
|
|
grades.result
|
|
from
|
|
grades
|
|
left join students on grades.student_id = students.id
|
|
left join courses on grades.course_id = courses.id;
|
|
```
|
|
|
|
You can run this instead:
|
|
|
|
```sql
|
|
select * from transcripts;
|
|
```
|
|
|
|
A view also behaves like a typical table. You can safely use it in table joins or create new views from existing views.
|
|
|
|
### Consistency
|
|
|
|
Views reduce the likelihood of mistakes when you execute a query repeatedly. In the example above, you might decide to exclude the course _Introduction to Postgres_. The query becomes:
|
|
|
|
```sql
|
|
select
|
|
students.name,
|
|
students.type,
|
|
courses.title,
|
|
courses.code,
|
|
grades.result
|
|
from
|
|
grades
|
|
left join students on grades.student_id = students.id
|
|
left join courses on grades.course_id = courses.id
|
|
where courses.code != 'PG101';
|
|
```
|
|
|
|
Without a view, you need to add the new rule to every dependent query. That increases the likelihood of errors and inconsistencies, and it takes considerable effort. With views, you alter the underlying query in the `transcripts` view, and the change applies to every application using it.
|
|
|
|
### Logical organization
|
|
|
|
With views, you can give your query a name. This is useful for teams working with the same database. Instead of guessing what a query does, a well-named view explains it. For example, the name of the `transcripts` view suggests that the underlying query involves the `students`, `courses`, and `grades` tables.
|
|
|
|
### Security
|
|
|
|
Views can restrict the amount and type of data presented to a user. Instead of giving a user direct access to a set of tables, you give them a view. You can prevent them from reading sensitive columns by excluding those columns from the underlying query.
|
|
|
|
## Materialized views
|
|
|
|
A [materialized view](https://www.postgresql.org/docs/current/rules-materializedviews.html) is a form of view that also stores its results to disk. Subsequent reads of a materialized view return results much faster than a conventional view, because the data is already available. A conventional view executes the underlying query each time you call it.
|
|
|
|
Using the example above, you can create a materialized view like this:
|
|
|
|
```sql
|
|
create materialized view transcripts as
|
|
select
|
|
students.name,
|
|
students.type,
|
|
courses.title,
|
|
courses.code,
|
|
grades.result
|
|
from
|
|
grades
|
|
left join students on grades.student_id = students.id
|
|
left join courses on grades.course_id = courses.id;
|
|
```
|
|
|
|
Reading from the materialized view is the same as a conventional view:
|
|
|
|
```sql
|
|
select * from transcripts;
|
|
```
|
|
|
|
## Refreshing materialized views
|
|
|
|
There's a trade-off: data in a materialized view isn't always up to date. Refresh it regularly to prevent the data from becoming too stale.
|
|
|
|
```sql
|
|
refresh materialized view transcripts;
|
|
```
|
|
|
|
How often you refresh a materialized view is up to you, and it probably differs for each view depending on its use case.
|
|
|
|
## Materialized views vs conventional views
|
|
|
|
Materialized views are useful when execution times for queries or views are too slow. This happens in views or queries that involve multiple tables and billions of rows. Use a materialized view only when you can tolerate outdated data. Internal dashboards and analytics are common use cases.
|
|
|
|
Creating a materialized view isn't a solution to inefficient queries. Always optimize a slow-running query, even when you implement a materialized view.
|
|
|
|
## Resources
|
|
|
|
- [Official Docs: Create view](https://www.postgresql.org/docs/current/sql-createview.html)
|
|
- [Postgres Tutorial: Views](https://www.postgresqltutorial.com/postgresql-views/)
|