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

PostgreSQL微观案例 |第41期

原创 多米爸比 2024-05-10
282

本期分享几个NULL相关的案例,包括:NULL与操作符、NULL与索引、NULL与UNIQUE字段、NULL与外键引用、NULL与默认值

1.NULL与操作符

当使用"||"连接符和(+, -, *, /)算术运算符,以及(=, <, >, <=, >=, <>)比较操作符时,如果有NULL值可能会引起结果丢失。

为了更安全的使用NULL,在做连接时需要使用COALESCE函数,做比较运算时,(=, <>)可替换为 IS [NOT] DISTINCT FROM,动态语句传递变量时需要使用format函数,集合操作NOT IN可替换为NOT EXISTS。

2.NULL与索引

使用=或<>操作符时,运算结果不一定是布尔值,可能有NULL值参与运算,确保返回布尔值需进行如下替换:

操作符 替换操作符
<> IS DISTINCT FROM
= IS NOT DISTINCT FROM

替换后的IS NOT DISTINCT FROM会绕过索引,如果既需要保证安全的布尔值,又需要走索引,可参考如下的SQL等价改写。

改写前的SQL

SELECT * FROM foo WHERE bar IS NOT DISTINCT FROM baz;
复制

改写后的SQL

SELECT * FROM foo WHERE (bar IS NULL AND baz IS NULL) OR bar = baz;
复制

3.NULL与UNIQUE字段

由于NULL != NULL,UNIQUE字段允许有多个NULL值,从版本15开始能控制UNIQUE字段是否允许有多个NULL值,语法如下:

UNIQUE NULLS DISTINCT UNIQUE NULLS NOT DISTINCT
复制

例如下面表字段column1只允许插入一个NULL值,使用UNIQUE NULLS NOT DISTINCT

CREATE TABLE tab1( column1 VARCHAR, UNIQUE NULLS NOT DISTINCT(column1) );
复制

15之前的版本可以使用带where条件的表达式索引来实现:

CREATE UNIQUE INDEX ON tab1 ((column1 is null)) WHERE column1 IS NULL;
复制

4.NULL与外键引用

如果外键引用多个字段时,可以使用num_nulls/num_nonnulls函数更好的定义约束,例如只允许外键引用字段列表里任意一个字段非空:

CHECK (num_nonnulls(column1, column2, column3) = 1);
复制

参考示例如下:

CREATE TABLE companies ( id serial primary key, name text ); CREATE TABLE orders ( id serial primary key ); CREATE TABLE users ( id serial primary key, name text ); CREATE TABLE notifications ( id serial, company_id INT REFERENCES companies (id), order_id INT REFERENCES orders (id), user_id INT REFERENCES users (id) );
复制

notifications表添加约束:要求外键引用的三个字段列表有一个字段不为NULL

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

版本15对外键引用多字段设置on delete级联删除时还允许对单个字段设置NULL值或者默认值,示例如下:

CREATE TABLE tab_pk ( tid int, id int, primary key (tid, id) ); CREATE TABLE tab_fk ( tid int, id int, fk_id_del_set_null int, fk_id_del_set_default int default 0, foreign key (tid, fk_id_del_set_null) references tab_pk on delete set null (fk_id_del_set_null), foreign key (tid, fk_id_del_set_default) references tab_pk on delete set default (fk_id_del_set_default) );
复制

5.NULL与默认值

MySQL迁移过来的表字段上有default NULL的设置,迁移工具做了如下转化处理:

CREATE TABLE tab(action varbinary default NULL::varbinary);
复制

增加了对NULL按实际类型转换的处理,这个默认值的处理对后续修改字段类型造成了影响。

ALTER TABLE tab ALTER COLUMN action TYPE varchar(8) USING action::varchar(8);
复制

上面的语句通常情况下是可以正常执行,不过由于默认值类型转换导致修改会失败:

ERROR: default for column "action" cannot be cast automatically to type varchar
复制

上面的默认值定义无实际用处,但造成了字段类型修改时报错,因此也需要先调整处理默认值。

可以将默认值的调整和字段类型的修改放在一个事务里进行操作:

ALTER TABLE tab ALTER COLUMN action DROP DEFAULT, ALTER COLUMN action TYPE varchar(8) USING action::varchar(8), ALTER COLUMN action SET DEFAULT NULL::varchar(8);
复制
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论

目录
  • 1.NULL与操作符
  • 2.NULL与索引
  • 3.NULL与UNIQUE字段
  • 4.NULL与外键引用
  • 5.NULL与默认值