两个令人兴奋的 PostgreSQL 特性可改善 NULL 处理#PG认证

唯一列中的 NULL 值永久链接

一个众所周知但令人讨厌的怪异值NULL是NULL != NULL,因此一UNIQUE列仍然可以有多个NULL值。
#PG培训#PG考试#postgresql培训#postgresql考试#postgresql认证
在这里插入图片描述

(为了简单起见,示例使用数字 id 列,但我通常更喜欢更复杂的 id,例如ULID。)

CREATE TABLE test (
    id serial PRIMARY KEY,
    value TEXT UNIQUE
);

INSERT INTO test (value) VALUES ('a');

-- This fails on the duplicate:
--   ERROR:  duplicate key value violates unique constraint "test_value_key"
--   DETAIL:  Key (value)=(a) already exists.
INSERT INTO test (value) VALUES ('a');

-- But this does not:
INSERT INTO test (value) VALUES (null);
INSERT INTO test (value) VALUES (null);

SELECT * from test;
 id | value
----+-------
  1 | a
  3 |
  4 |
(3 rows)

然而,PostgreSQL 15 发布了一项新功能,可以改变这种行为UNIQUE NULLS NOT DISTINCT::

CREATE TABLE test (
    id serial PRIMARY KEY,
    value TEXT UNIQUE NULLS NOT DISTINCT
);

-- Now this fails on the second insert:
--   ERROR:  duplicate key value violates unique constraint "test_value_key"
--   DETAIL:  Key (value)=(null) already exists.
INSERT INTO test (value) VALUES (null);
INSERT INTO test (value) VALUES (null);

确保单个列有值永久链接

我遇到的一个常见用例是,一个表有多个外键,但只需要填充一个。例如,假设我们有一个notifications表来表示我们发出的通知(例如电子邮件、短信等)。这些通知可能会被触发并与我们系统中的特定实体相关,例如订单、用户、公司等。我们想添加外键来表示此通知的用途,但我们只想填充其中一个。

例如:

CREATE TABLE notifications (
    id serial,
    company_id INT REFERENCES companies (id),
    order_id INT REFERENCES orders (id),
    user_id INT REFERENCES users (id)
);

INSERT INTO notifications (company_id) VALUES (100);
INSERT INTO notifications (order_id) VALUES (200);

SELECT * from notifications;

 id | company_id | order_id | user_id
----+------------+----------+---------
  1 |        100 |          |
  2 |            |      200 |
(2 rows)

通常,我会看到像这样的表添加约束以确保至少有一列被填充:

ALTER TABLE notifications
ADD CONSTRAINT notifications_reference
CHECK (company_id IS NOT NULL OR order_id IS NOT NULL OR user_id IS NOT NULL);

但是,这并不能阻止您意外填充多个列。如果您使用为您生成 SQL 的对象关系映射器 (ORM),并且您意外设置了对象的多个属性,则很容易发生这种情况:

INSERT INTO notifications (company_id, order_id, user_id)
VALUES (NULL, 300, 400);

SELECT * from notifications;

 id | company_id | order_id | user_id
----+------------+----------+---------
  1 |        100 |          |
  2 |            |      200 |
  3 |            |      300 |     400
(3 rows)

不过,有一对 PostgreSQL 函数可以让我们编写更好的约束检查,称为num_nulls/num_nonnulls。这让我们可以检查一组列中是否只有一个非 NULL 值。例如:

ALTER TABLE notifications
ADD CONSTRAINT notifications_reference
CHECK (num_nonnulls(company_id, order_id, user_id) = 1);

-- Now we get an error on insert if there is more than one value:
--   ERROR:  new row for relation "notifications" violates check constraint "notifications_reference"
--   DETAIL:  Failing row contains (3, null, 300, 400).
INSERT INTO notifications (company_id, order_id, user_id)
VALUES (NULL, 300, 400);

-- Or if there are no values:
--   ERROR:  new row for relation "notifications" violates check constraint "notifications_reference"
--   DETAIL:  Failing row contains (4, null, null, null).
INSERT INTO notifications (company_id, order_id, user_id)
VALUES (NULL, NULL, NULL);

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值