数据库

查询关联表与嵌套表


数据API会自动检测Postgres表之间的关系。由于Postgres是一个关系型数据库,这是非常常见的情况。

一对多关联

让我们使用一个存储orchestral_sections(乐团声部)和instruments(乐器)的示例数据库:

乐团声部

idname
1弦乐组
2木管组

乐器

idnamesection_id
1小提琴1
2中提琴1
3长笛2
4双簧管2

API会根据外键自动检测表之间的关系:

1
2
3
4
5
const { data, error } = await supabase.from('orchestral_sections').select(` id, name, instruments ( id, name )`)

用于联表查询的 TypeScript 类型

supabase-js 总是返回一个 data 对象(表示成功请求)和一个 error 对象(表示失败请求)。

这些辅助类型提供了任何查询的结果类型,包括数据库联表查询的嵌套类型。

给定以下包含管弦乐声部与乐器关系的数据库模式:

1
2
3
4
5
6
7
8
9
10
create table orchestral_sections ( "id" serial primary key, "name" text);create table instruments ( "id" serial primary key, "name" text, "section_id" int references "orchestral_sections");

我们可以像这样获取嵌套的 SectionsWithInstruments 类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { QueryResult, QueryData, QueryError } from '@supabase/supabase-js'const sectionsWithInstrumentsQuery = supabase.from('orchestral_sections').select(` id, name, instruments ( id, name )`)type SectionsWithInstruments = QueryData<typeof sectionsWithInstrumentsQuery>const { data, error } = await sectionsWithInstrumentsQueryif (error) throw errorconst sectionsWithInstruments: SectionsWithInstruments = data

多对多关联

数据API会自动检测多对多关联关系。例如,如果您有一个存储用户团队关系的数据库(每个用户可以属于多个团队):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
create table users ( "id" serial primary key, "name" text);create table teams ( "id" serial primary key, "team_name" text);create table members ( "user_id" int references users, "team_id" int references teams, primary key (user_id, team_id));

在这种情况下,您不需要显式定义连接表(members)。如果我们想获取所有团队及其成员:

1
2
3
4
5
const { data, error } = await supabase.from('teams').select(` id, team_name, users ( id, name )`)

为多外键连接指定 ON 子句

例如,如果您有一个记录员工上下班打卡的项目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 员工表create table users ( "id" serial primary key, "name" text);-- 工牌扫描记录create table scans ( "id" serial primary key, "user_id" int references users, "badge_scan_time" timestamp);-- 工作班次create table shifts ( "id" serial primary key, "user_id" int references users, "scan_id_start" int references scans, -- 上班打卡 "scan_id_end" int references scans, -- 下班打卡 "attendance_status" text);

这种情况下,您需要明确定义连接条件,因为 shifts 表上的连接列存在歧义(它们都引用了 scans 表)。

要获取所有与特定 scan 相关的 shifts 记录(包含 scan_id_startscan_id_end),请使用以下语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const { data, error } = await supabase.from('shifts').select( ` *, start_scan:scans!scan_id_start ( id, user_id, badge_scan_time ), end_scan:scans!scan_id_end ( id, user_id, badge_scan_time ) `)