跳转至

PostgreSQL Data Defination

参考:PostgreSQL: Documentation: 15: 5.4. Constraints

二、默认值

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE products (
    product_no integer,
    name text,
    price numeric DEFAULT 9.99
);

默认值也可以被设置为一个表达式,表达式会在记录被插入时求值。

一个例子就是时间戳 DEFAULT CURRENT_TIMESTAMP,还有就是自增的序列号:

PostgreSQL SQL Dialect
1
2
3
4
CREATE TABLE products (
    product_no integer DEFAULT nextval('products_product_no_seq'),
    ...
);

这个 nextval() 函数见 PostgreSQL: Documentation: 15: 9.17. Sequence Manipulation Functions

四、Constraints 约束

一、Check 约束

check 约束是最通用的约束类型,规定某一列必须满足一个布尔表达式。

比如要求产品价格 price 必须为正数:

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CHECK (price > 0)
);

可以使用 CONSTRAINT 关键字来指定约束的名字:

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CONSTRAINT positive_price CHECK (price > 0)
);

上面的是 对于某一列的约束,还可以添加 对整张表的约束

PostgreSQL SQL Dialect
1
2
3
4
5
6
7
CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CHECK (price > 0),
    discounted_price numeric CHECK (discounted_price > 0),
    CHECK (price > discounted_price)
);

额外的约束并没有紧接着写在某一列后面,而是单独出现在列的列表中。

对某一列的约束应当只引用当前列,而对整张表的约束可以引用多个列(虽然 PostgreSQL 并不强制,但是其他 SQL 可能会强制要求)。

下面是等价的一些其他写法:

PostgreSQL SQL Dialect
1
2
3
4
5
6
7
8
9
CREATE TABLE products (
    product_no integer,
    name text,
    price numeric,
    CHECK (price > 0),
    discounted_price numeric,
    CHECK (discounted_price > 0),
    CHECK (price > discounted_price)
);
PostgreSQL SQL Dialect
1
2
3
4
5
6
7
CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CHECK (price > 0),
    discounted_price numeric,
    CHECK (discounted_price > 0 AND price > discounted_price)
);

同样对于表的约束也可以使用 CONSTRAINT 关键字指定约束名。

二、Not-Null 约束

要求某一列不能为空值。

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE products (
    product_no integer NOT NULL,
    name text NOT NULL,
    price numeric
);

其实等价于:

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE products (
    product_no integer CHECK (product_no is NOT NULL),
    name text CHECK (name is NOT NULL),
    price numeric
);

多个约束条件可以用空格隔开这么写:

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE products (
    product_no integer NOT NULL,
    name text NOT NULL,
    price numeric NOT NULL CHECK (price > 0)
);

3. Unique 约束

要求某一列的值不重复。

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE products (
    product_no integer UNIQUE,
    name text UNIQUE,
    price numeric
);

写作对表的约束可以这么写:

PostgreSQL SQL Dialect
1
2
3
4
5
6
CREATE TABLE products (
    product_no integer,
    name text,
    price numeric,
    UNIQUE (product_no, name)
);

要注意的是 NULL 被视为不相同,也就是说如果两条记录的 Unique 约束内的某一列都为 NULL,是不违反约束的。可以通过添加 NULLS NOT DISTINCT 来规定将 NULL 值视为相等:

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE products (
    product_no integer UNIQUE NULLS NOT DISTINCT,
    name text,
    price numeric
);

4. Primary Keys

一个主键唯一确定一条记录,也就是 UNIQUE 且 NOT NULL。

所以

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);

其实等价于

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE products (
    product_no integer UNIQUE NOT NULL,
    name text,
    price numeric
);

可以以一组列作为主键:

PostgreSQL SQL Dialect
1
2
3
4
5
6
CREATE TABLE example (
    a integer,
    b integer,
    c integer,
    PRIMARY KEY (a, c)
);

5. Foreign Keys

外键必须在其他表中存在,即参照完整性。

比如对于这样一张产品表 products:

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);

其中的 product_no 可能要被其他表引用,比如订单表 orders:

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    product_no integer REFERENCES products (product_no),
    quantity integer
);

这时,如果新的记录的 product_no 在 products 表中不存在则会违反约束。

上面的命令也可以简写:

PostgreSQL SQL Dialect
1
2
3
4
5
CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    product_no integer REFERENCES products,
    quantity integer
);

因为对于另一个表的引用其实默认就是以那个表的主键为引用列的。

也可以引用多个列:

PostgreSQL SQL Dialect
1
2
3
4
5
6
CREATE TABLE t1 (
  a integer PRIMARY KEY,
  b integer,
  c integer,
  FOREIGN KEY (b, c) REFERENCES other_table (c1, c2)
);

当然数量和类型必须对应。


有时候外键会是自己同一张表的主键:

PostgreSQL SQL Dialect
1
2
3
4
5
6
CREATE TABLE tree (
    node_id integer PRIMARY KEY,
    parent_id integer REFERENCES tree,
    name text,
    ...
);

这叫做 自引用外键


还会有一个问题,就是如果引用的外键在其他表中对应记录被删除呢?此时这个记录就不合法了。

这是有几个选择:

  • 不允许删除作为外键被引用的记录 ON DELETE RESTRICT
  • 将引用了外键的记录的记录也删除掉 ON DELETE CASACDE
PostgreSQL SQL Dialect
CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    shipping_address text,
    ...
);

CREATE TABLE order_items (
    product_no integer REFERENCES products ON DELETE RESTRICT,
    order_id integer REFERENCES orders ON DELETE CASCADE,
    quantity integer,
    PRIMARY KEY (product_no, order_id)
);

如果 products 中的某条记录被引用,那么不允许删除 products 中的该条记录。

如果 orders 中的某条记录被删除,那么 order_items 引用了对应记录的键的记录就会被删除。

还有一些其他的:

如果什么都不写就是 NO ACTION,会抛出错误。

还有 SET NULLSET DEFAULT xxx,顾名思义。

这块还有点复杂,先咕一下,后面用到了再详细整理。

6. Exclusion 约束

五、修改表

1. 添加列

PostgreSQL SQL Dialect
ALTER TABLE products ADD COLUMN description text;

2. 删除列

PostgreSQL SQL Dialect
ALTER TABLE products DROP COLUMN description;

对于被引用的记录,可以使用 CASCADE 来删除依赖于此列的列:

PostgreSQL SQL Dialect
ALTER TABLE products DROP COLUMN description CASCADE;

3. 添加约束

PostgreSQL SQL Dialect
1
2
3
ALTER TABLE products ADD CHECK (name <> '');
ALTER TABLE products ADD CONSTRAINT some_name UNIQUE (product_no);
ALTER TABLE products ADD FOREIGN KEY (product_group_id) REFERENCES product_groups;

对于 NOT NULL 这种不能被写为表约束的约束,可以用修改列的语法来写:

PostgreSQL SQL Dialect
ALTER TABLE products ALTER COLUMN product_no SET NOT NULL;

4. 删除约束

PostgreSQL SQL Dialect
ALTER TABLE products DROP CONSTRAINT some_name;

对于 NOT NULL

PostgreSQL SQL Dialect
ALTER TABLE products ALTER COLUMN product_no DROP NOT NULL;

5. 改变某一列的默认值

PostgreSQL SQL Dialect
ALTER TABLE products ALTER COLUMN price SET DEFAULT 7.77;

或者删除默认值:

PostgreSQL SQL Dialect
ALTER TABLE products ALTER COLUMN price DROP DEFAULT;

6. 改变某一列的数据类型

PostgreSQL SQL Dialect
ALTER TABLE products ALTER COLUMN price TYPE numeric(10,2);

7. 重命名某一列

PostgreSQL SQL Dialect
ALTER TABLE products RENAME COLUMN product_no TO product_number;

8. 重命名表

PostgreSQL SQL Dialect
ALTER TABLE products RENAME TO items;

评论