暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

[译]What is a schema in PostgreSQL?

原创 pgbase 2023-06-11
242

模式是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

如你所见,所有依赖对象已经被删除。我们得到一个干净、一致的数据库,它不包含任何过时或无效的对象。

 

最后,即使你没有意识到,模式总是游戏的一部分;它提供了一种很好的方式来组织数据,使得数据更清晰和便于理解。

最后修改时间:2023-06-13 17:15:38
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论