PostgreSQL 三价逻辑详解

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

背景

在逻辑运算中有三种状态表示,真、假、不知道。

数据库的NULL表示没有值, 空的意思(在逻辑中属于 不知道)。

在三价逻辑运算中, 数据库的NULL相当于UNKNOWN的意思.

三价逻辑运算请参考 :

http://en.wikipedia.org/wiki/Three-valued_logic

来看看三价逻辑运算的真值表 :

Kleene logic

Below is a set of truth tables showing the logic operations for Kleene's logic.

A AND B True Unknown False
True True Unknown False
Unknown Unknown Unknown False
False False False False
A OR B True Unknown False
True True True True
Unknown True Unknown Unknown
False True Unknown False
A NOT A
True False
Unknown Unknown
False True

Lukasiewicz logic真值表略...

简单的解释一下, 在这里Unknown可能是true也可能是false。

因此 :

对于Unknown and true 可能是true and true也可能是false and true. 那么结果应该是true或者false. 最终还是不确定. 所以还是Unknown.

对于NOT Unknown. 也一样, 可能是NOT true也可能是NOT false, 结果有可能是true或者false, 最终还是不确定, 所以还是Unknown.

对于Unknown or true, 不管Unknown是true还是false, 结果都是true.

对于Unknown and false, 不管Unknown是true还是false, 结果都是false.

对于Unknown and Unknown, 可能是true and true或者true and false或者false and false最终结果不确定, 所以还是Unknown.

对于Unknown or Unknown, 可能是true or true或者true or false或者false or false最终结果不确定, 所以还是Unknown.

在PostgreSQL数据库中是如何处理的呢?

正文

验证以上真值表(仅验证含null的部分) :

ocz@db-172-16-3-150-> psql digoal  
psql (9.2.1)  
Type "help" for help.  
digoal=# \pset null UnKnown  
Null display is "UnKnown".  
digoal=# select null and true;  
 UnKnown  
digoal=# select null and null;  
 UnKnown  
digoal=# select null and false;  
 f  
digoal=# select null or true;  
 t  
digoal=# select null or null;  
 UnKnown  
digoal=# select null or false;  
 UnKnown  
digoal=# select not null;  
 UnKnown  

接下来测试where条件中的null.

digoal=# select 1 where null;  
 ?column?   
----------  
(0 rows)  
digoal=# select 1 where true;  
 ?column?   
----------  
        1  
(1 row)  
digoal=# select 1 where false;  
 ?column?   
----------  
(0 rows)  

从测试结果可以看出WHERE子句中的 null和false一样, 被排除掉了.

接下来测试数据库基本的比较操作中用到null的地方的运算结果.

Operator Description
< less than
> greater than
<= less than or equal to
>= greater than or equal to
= equal
<> or != not equal

以上操作符只要带有null的比较返回值都是null。

例如 :

digoal=# select 1 < null;  
 ?column?   
----------  
 UnKnown  
(1 row)  
digoal=# select true < null;  
 ?column?   
----------  
 UnKnown  
(1 row)  

表示不知道null和null是否相等, 所以输出还是null.

digoal=# select null=null;  
 ?column?   
----------  
 UnKnown  
(1 row)  

表示不知道null和null是否不相等, 所以输出还是null.

digoal=# select null <> null;  
 ?column?   
----------  
 UnKnown  
(1 row)  

between and 相当于>= and <=, 例如 :

digoal=# select 1 between null and null;  
 ?column?   
----------  
 UnKnown  
(1 row)  
digoal=# select 1 between null and 2;  
 ?column?   
----------  
 UnKnown  
(1 row)  
digoal=# select null between null and 2;  
 ?column?   
----------  
 UnKnown  
(1 row)  
digoal=# select null between 1 and 2;  
 ?column?   
----------  
 UnKnown  
(1 row)  
digoal=# select null between 1 and null;  
 ?column?   
----------  
 UnKnown  
(1 row)  

not between and 相当于 < or >, 例如 :

digoal=# select null not between 1 and null;  
 ?column?   
----------  
 UnKnown  
(1 row)  

2. case, 注意CASE中如果使用NULL, 是使用的=操作符. 所以null分支用于不会执行. 如下 :

digoal=# select case 1 when null then 'is null' else 'is not null' end;  
    case       
-------------  
 is not null  
(1 row)  
digoal=# select case null when null then 'is null' else 'is not null' end;  
    case       
-------------  
 is not null  
(1 row)  

其他表达式中的null :

表示不知道null+1等于多少, 结果输出还是null.

postgres=# select null+1;  
 ?column?   
----------  
  UnKnown  
(1 row)  
digoal=# select null||'abc';  
 ?column?   
----------  
 UnKnown  
(1 row)  
digoal=# select 'abc'||null;  
 ?column?   
----------  
 UnKnown  
(1 row)  

注意

1. SQL中的特例, 某些使用场景中null和null被认为是相同的, 这打破了三价逻辑的规则. 如下.

postgres=# select null union select null;  
 ?column?   
----------  
 UnKnown  
(1 row)  
postgres=# select null intersect select null;  
 ?column?   
----------  
 UnKnown  
(1 row)  
digoal=# select null except select null;  
 ?column?   
----------  
(0 rows)  

2. 聚合函数, 除了count(*) 其他聚合函数都不处理null值. 例如 :

Table

i j
150 150
200 200
250 250
NULL 0

Here AVG(i) is 200 (the average of 150, 200, and 250), while AVG(j) is 150 (the average of 150, 200, 250, and 0).

A well-known side effect of this is that in SQL AVG(z) is not equivalent with SUM(z)/COUNT(*).

聚合函数注意, 如果传入了distinct的话, 就要看函数的strict标记 :

     * Aggregate functions that are called with DISTINCT are now passed  
       NULL values if the aggregate transition function is not marked as  
       STRICT (Andrew Gierth)  
       For example, agg(DISTINCT x) might pass a NULL x value to agg().  
       This is more consistent with the behavior in non-DISTINCT cases.  

3. 在SQL92标准扩展文件F571定义了6个操作符, 仅返回true或false, 不返回unknown如下 :

p true false unknown
p IS TRUE true false false
p IS NOT TRUE false true true
p IS FALSE false true false
p IS NOT FALSE true false true
p IS UNKNOWN false false true
p IS NOT UNKNOWN true true false
digoal=# select null is true;  
 ?column?   
----------  
 f  
(1 row)  
digoal=# select null is not true;  
 ?column?   
----------  
 t  
(1 row)  
digoal=# select null is not false;  
 ?column?   
----------  
 t  
(1 row)  
digoal=# select null is false;  
 ?column?   
----------  
 f  
(1 row)  
digoal=# select null is null;  
 ?column?   
----------  
 t  
(1 row)  
digoal=# select null is not null;  
 ?column?   
----------  
 f  
(1 row)  
digoal=# select null is not unknown;  
 ?column?   
----------  
 f  
(1 row)  
digoal=# select null is unknown;  
 ?column?   
----------  
 t  
(1 row)  

4. 除此之外, PostgreSQL 中还包含两个逻辑操作符.

IS DISTINCT FROM 和 IS NOT DISTINCT FROM, 所有操作都返回true或者false, 不会返回null :

Ordinary comparison operators yield null (signifying "unknown"), not true or false, when either input is null.   

For example, 7 = NULL yields null, as does 7 <> NULL. When this behavior is not suitable, use the IS [ NOT ] DISTINCT FROM constructs:  

expression IS DISTINCT FROM expression expression IS NOT DISTINCT FROM expression For non-null inputs, IS DISTINCT FROM is the same as the <> operator.   

However, if both inputs are null it returns false, and if only one input is null it returns true.   

Similarly, IS NOT DISTINCT FROM is identical to = for non-null inputs, but it returns true when both inputs are null, and false when only one input is null.   

Thus, these constructs effectively act as though null were a normal data value, rather than "unknown".  
digoal=# select null is distinct from null;  
 ?column?   
----------  
 f  
(1 row)  
digoal=# select null is distinct from 1;  
 ?column?   
----------  
 t  
(1 row)  
digoal=# select null is not distinct from 1;  
 ?column?   
----------  
 f  
(1 row)  
digoal=# select null is not distinct from null;  
 ?column?   
----------  
 t  
(1 row)  

5. PostgreSQL transform_null_equals参数打开, 将'表达式=null'或'null=表达式'转换成'表达式 is null'. 如下 :

digoal=# set transform_null_equals=on;  
SET  
digoal=# select 1=null;  
 ?column?   
----------  
 f  
(1 row)  
digoal=# select null=null;  
 ?column?   
----------  
 t  
(1 row)  
digoal=# select null=1;  
 ?column?   
----------  
 f  
(1 row)  

注意这个参数不影响case表达式的判断, 和修改前结果一致 :

digoal=# select case null when null then 'is null' else 'is not null' end;  
    case       
-------------  
 is not null  
(1 row)  

6. greatest和least不处理null值.

digoal=# select greatest(null,null);  
 greatest   
----------  
 UnKnown  
(1 row)  
digoal=# select least(null,null);  
  least    
---------  
 UnKnown  
(1 row)  
digoal=# select least(null,null,1,2,3);  
 least   
-------  
     1  
(1 row)  
digoal=# select greatest(null,null,1,2,3);  
 greatest   
----------  
        3  
(1 row)  

7. 索引中的null值, PostgreSQL 8.3以前(不含8.3)的版本的BTREE索引不支持IS NULL的查询.

参见 HISTORY :

     * Allow col IS NULL to use an index (Teodor)  

参考

1. http://en.wikipedia.org/wiki/Null_(SQL)

2. http://en.wikipedia.org/wiki/Three-valued_logic

3. http://www.databasedesign-resource.com/null-values-in-a-database.html

4. http://en.wikipedia.org/wiki/Propositional_logic

5. http://en.wikipedia.org/wiki/%C5%81ukasiewicz_logic

6. http://en.wikipedia.org/wiki/Stephen_Cole_Kleene

7. http://link.springer.com/chapter/10.1007%2F3-540-36596-6_7

8. http://www.postgresql.org/docs/9.2/static/functions-comparison.html

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
关系型数据库 流计算 PostgreSQL
关于PostgreSQL逻辑订阅中的复制状态
关于PostgreSQL逻辑订阅中的复制状态
2425 0
|
3月前
|
SQL 关系型数据库 分布式数据库
PolarDB for PostgreSQL逻辑复制问题之逻辑复制冲突如何解决
PolarDB for PostgreSQL是基于PostgreSQL开发的一款云原生关系型数据库服务,它提供了高性能、高可用性和弹性扩展的特性;本合集将围绕PolarDB(pg)的部署、管理和优化提供指导,以及常见问题的排查和解决办法。
|
5月前
|
存储 人工智能 关系型数据库
postgresql从入门到精通教程 - 第36讲:postgresql逻辑备份
postgresql从入门到精通教程 - 第36讲:postgresql逻辑备份
156 1
|
10月前
|
监控 关系型数据库 API
PostgreSQL 13、14中逻辑复制/解码改进
PostgreSQL 13、14中逻辑复制/解码改进
145 0
|
10月前
|
存储 关系型数据库 分布式数据库
PostgreSQL 14中两阶段提交的逻辑解码
PostgreSQL 14中两阶段提交的逻辑解码
125 0
|
10月前
|
SQL 机器学习/深度学习 存储
PostgreSQL逻辑备份pg_dump使用及其原理解析
PostgreSQL逻辑备份pg_dump使用及其原理解析
184 0
|
存储 SQL 安全
2 PostgreSQL 物理,逻辑,进程结构以及系统表系统函数|学习笔记
快速学习2 PostgreSQL 物理,逻辑,进程结构以及系统表系统函数
320 0
2 PostgreSQL 物理,逻辑,进程结构以及系统表系统函数|学习笔记
|
存储 容灾 关系型数据库
7 PostgreSQL 物理备份和还原,逻辑备份和还原|学习笔记
快速学习7 PostgreSQL 物理备份和还原,逻辑备份和还原
994 0
7 PostgreSQL 物理备份和还原,逻辑备份和还原|学习笔记
|
关系型数据库 数据库 数据安全/隐私保护
【RDS PostgreSQL】逻辑订阅
RDS PostgreSQL提供逻辑订阅功能,支持在多个RDS PostgreSQL实例之间实现准实时表级单向同步,可以用于数据共享、数据汇总、数据拆分等业务场景。 如果您在全国多个地域部署业务,可以通过逻辑订阅共享数据,例如将中心数据共享给其他地域,或者将其他地域数据汇总到中心进行实时分析查询等。 本文简单介绍逻辑订阅的使用流程。
189 0
【RDS PostgreSQL】逻辑订阅
|
SQL Oracle 关系型数据库
PostgreSQL 使用逻辑decode实现异步主从切换后,时间线分歧变化量补齐、修复
PostgreSQL 使用逻辑decode实现异步主从切换后,时间线分歧变化量补齐、修复
1297 0

相关产品

  • 云原生数据库 PolarDB