模式是PostgreSQL用来组织数据的一种方式。什么是模式?更重要的是:使用模式的目的是什么以及如何使用模式使你的生活更简单?让我们深入探索这个问题。
模式的目的
在你想出如何使用模式之前,首先要知道使用模式的目的。为了便于理解,首先让我们看先PostgreSQL的结构:
实例
数据库
模式
表
行
实例大体上是PostgreSQL部署后的启动形态。下一层是数据库。在现实中数据库就是你访问的:在PostgreSQL中连接总是与实例中的一个数据库绑定,通过用户认证后,会创建连接。
重要的是下一层,在数据库和表之间:模式。
使用模式组织表
基本上,模式是一种组织表的方式。假设有一个相当大的数据结构:500张表放在一起比分成十份每份50张表要更难管理和理解。
这有点像组织照片:你不会将所有照片放在一个目录,二是将他们根据年份放在不同目录下。同样的逻辑可以应用在管理表上。
模式和PostgreSQL
现在我将重点放在模式如何应用到PostgreSQL中。我们必须了解第一件事是public模式。
使用public模式
PostgreSQL的美妙之处是即便你对模式一无所知,你仍然可以使用它。其中的原因是PostgreSQL中默认有一个public模式。我们如何知道PostgreSQL中有哪些模式呢?psql提供了\dn命令来展示这个信息:
demo=# \dn
List of schemas
Name |
Owner
--------+-------------------
public | pg_database_owner
(1 row)
默认情况下,表会创建在public模式下,举例如下:
demo=# CREATE TABLE t_product (
id
serial,
name
text,
price numeric
);
CREATE TABLE
这是一张基础表。Psql中可以使用\d来查看模式下的表:
demo=# \d
List of relations
Schema |
Name
|
Type | Owner
--------+------------------+----------+-------
public |
t_product | table
| hs
public | t_product_id_seq | sequence | hs
(2 rows)
在上述例子中,表和序列都创建在模式模式中。如你所见,你不要任何关于模式的知识。如果你碰巧使用了public模式,我们建议你确认下新版本PostgreSQL中的安全策略。
总的来说,有两种方式可以查询表。
第一种方式是:
demo=# SELECT * FROM t_product;
id | name | price
----+------+-------
(0 rows)
另一种查询表的方式是:
你可以显示的在表名前使用模式前缀,他们组合在一起是一个完整的表名。我看过很多ORM框架这样来引用表,以减少错误配置引用错表的风险。我们稍后也会在本文中看到:
demo=# SELECT * FROM public.t_product;
id | name | price
----+------+-------
(0 rows)
这是public模式的简单介绍,我们继续往下,创建第一个新模式。
创建模式
如何在PostgreSQL中创建模式?create
schema命令就是答案:
demo=# \h CREATE SCHEMA
Command:
CREATE SCHEMA
Description: define a new
schema
Syntax:
CREATE SCHEMA schema_name
[ AUTHORIZATION role_specification ]
[ schema_element [ ... ]
]
CREATE SCHEMA AUTHORIZATION role_specification
[ schema_element [ ... ]
]
CREATE SCHEMA IF NOT EXISTS schema_name
[ AUTHORIZATION role_specification ]
CREATE SCHEMA IF NOT EXISTS AUTHORIZATION role_specification
where role_specification can be:
user_name
| CURRENT_ROLE
| CURRENT_USER
| SESSION_USER
URL: https://www.postgresql.org/docs/15/sql-createschema.html
语法十分简单,并允许我们同时定义模式名称和模式属主。否则,语法将变得简洁明了:
demo=# CREATE SCHEMA warehouse;
CREATE SCHEMA
一旦模式被创建,我就可以在模式下创建表:
demo=# CREATE TABLE warehouse.t_product (
prod_number text PRIMARY KEY,
d
date,
in_stock
int
);
CREATE TABLE
通过模式名做前缀引用表,你可以选择你想使用的模式。需要注意的是模式本身并不影响数据存储方式。与表相关的数据文件仍然存放在PostgreSQL数据目录中。因此模式不会影响性能和存储优化。模式的目的是将对象分组,以及通过模式授权来制定安全策略。
demo=# \d
warehouse.t_product;
Table "warehouse.t_product"
Column
|
Type | Collation | Nullable | Default
-------------+---------+-----------+----------+---------
prod_number |
text
| | not null |
d
| date
|
| |
in_stock
| integer |
| |
Indexes:
"t_product_pkey" PRIMARY KEY, btree (prod_number)
这里有两点需要指出:首先,不同的模式中可能存在同名的两张表。如public.t_product和warehouse.t_product表。这种情况十分常见。其次是public模式下的表不需要模式前缀引用。原因在于下面这个参数:
demo=# SHOW search_path;
search_path
-----------------
"$user", public
(1 row)
所有search_path下的对象都可以直接访问,无需模式名前缀引用。我们可以简单测试下:
demo=# SET search_path TO warehouse;
SET
注意该参数只是在会话修改,如上操作不会影响你的生产系统
从现在开始,第二张名为t_product的表不再显示,因为PostgreSQL知道在哪个模式中查找该表:
demo=# \d t_product
Table "warehouse.t_product"
Column
|
Type | Collation | Nullable | Default
-------------+---------+-----------+----------+---------
prod_number |
text
| | not null |
d
| date
|
| |
in_stock
| integer |
| |
Indexes:
"t_product_pkey" PRIMARY KEY, btree (prod_number)
现在search_path已经该表,我们必须通过模式前缀来引用public模式下的t_product表,该表不在搜索路径中。
demo=# \d public.t_product
Table "public.t_product"
Column | Type
| Collation | Nullable
|
Default
--------+---------+-----------+----------+-----------------...
id
| integer |
| not null | …
name | text
|
| |
price | numeric | | |
简单介绍模式之后,让我们看下在视图中使用模式会如何:
视图与模式
视图是一种便于开发者访问数据的方式。重点在于模式正常情况下不是障碍。一个查询可以关联来自不同模式的表,视图可以通过该查询来检索模式中的数据(假设你有访问权限)。
demo=# SET search_path TO default;
SET
demo=# CREATE VIEW public.v AS
SELECT *
FROM warehouse.t_product ;
CREATE VIEW
然而,对想要从Oracle迁移到PostgreSQL的朋友有些影响。
PostgreSQL中重命名模式
在PostgreSQL中,任何可以创建的对象都允许修改名称。模式同样如此:
demo=# \h
ALTER SCHEMA
Command:
ALTER SCHEMA
Description: change the
definition of a schema
Syntax:
ALTER SCHEMA name RENAME TO new_name
ALTER SCHEMA name OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
URL: https://www.postgresql.org/docs/15/sql-alterschema.html
重命名模式会对视图有如下影响。记住重命名模式对视图的影响:
demo=# ALTER SCHEMA warehouse RENAME TO inventory;
ALTER SCHEMA
demo=# \d+ v
View "public.v"
Column
|
Type | Collation | Nullable | Default | Storage | …
-------------+---------+-----------+----------+---------+----------+
…
prod_number |
text
|
|
| | extended |
d
| date
|
|
| | plain |
in_stock
| integer |
|
| | plain |
View definition:
SELECT t_product.prod_number,
t_product.d,
t_product.in_stock
FROM inventory.t_product;
视图不直接引用表,视图引用内部对象ID,这一点尤为重要,因为重命名模式仅仅意味着给一个内部ID附上一个文本标签。视图定义不依赖名称,所以重命名对象不会使视图无效。相比之下,在其他数据库如Oracle中,重命名对象会使视图无效。
删除模式
在PostgreSQL中删除模式遵循相同的逻辑:
demo=# \h
DROP SCHEMA
Command:
DROP SCHEMA
Description: remove a schema
Syntax:
DROP SCHEMA [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]
URL: https://www.postgresql.org/docs/15/sql-dropschema.html
模式依赖
我们不想遗弃对象,所以我们不能仅仅删除他们。删除模式时,PostgreSQL会告诉我们会发生什么,但不会真的删除表-为了防止破坏模式上的依赖:
demo=# DROP SCHEMA inventory;
ERROR: cannot drop schema inventory because other
objects depend on it
DETAIL: table inventory.t_product
depends on schema inventory
view v depends on table inventory.t_product
HINT: Use DROP ... CASCADE to drop the dependent objects
too.
若我们真的想删除模式且模式存在依赖关系,可以在drop时加上CASCADE选项:
demo=# DROP SCHEMA inventory CASCADE;
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to table inventory.t_product
drop cascades to view v
DROP SCHEMA
如你所见,所有依赖对象已经被删除。我们得到一个干净、一致的数据库,它不包含任何过时或无效的对象。
最后,即使你没有意识到,模式总是游戏的一部分;它提供了一种很好的方式来组织数据,使得数据更清晰和便于理解。




