Oracle、MySQL、PostGreSQL、SQL Server-空值

news2024/10/5 20:23:33

Oracle、MySQL、PostGreSQL、SQL Server-null value

最近几年数据库市场百花齐放,在做跨数据库迁移的数据库选型时,除了性能、稳定、安全、运维、功能、可扩展外,像开发中对于值的处理往往容易被人忽视, 之前写过一篇关于PG区别Oracle在SQL解析缓存的笔记《PostgreSQL 12 : Prepare statement和plan_cache_mode 参数》,这里记录一下null 值在这几个数据库中的区别。

软件版本:Oracle 21c 、SQL Server 2019 、MySQL 8.0 、Mariadb 10.6 、PostGreSQL 13、OpenGauss 2.0

创建测试用例表

CREATE TABLE tab_null(id int, name char(10));
 
INSERT INTO tab_null VALUES(1,'anbob');
INSERT INTO tab_null VALUES(2,NULL);

测试数据过滤

# oracle
SQL> select * from tab_null where name is null;
        ID NAME
---------- ----------
         2

SQL> select * from tab_null where name is not null;
        ID NAME
---------- ----------
         1 anbob

SQL> select * from tab_null where name=null;
no rows selected

SQL> select * from tab_null where null=null;
no rows selected

# postgresql
postgres=# select * from tab_null where name is null;
 id | name
----+------
  2 |
(1 row)

postgres=# select * from tab_null where name is not null;
 id |    name
----+------------
  1 | anbob
(1 row)

postgres=# select * from tab_null where name=null;
 id | name
----+------
(0 rows)

postgres=# select * from tab_null where null=null;
 id | name
----+------
(0 rows)

# MySQL/MariaDB
mysql> select * from tab_null where name is null;
+------+------+
| id   | name |
+------+------+
|    2 | NULL |
+------+------+
1 row in set (0.00 sec)

mysql> select * from tab_null where name is not null;
+------+-------+
| id   | name  |
+------+-------+
|    1 | anbob |
+------+-------+
1 row in set (0.00 sec)

mysql> select * from tab_null where name=null;
Empty set (0.00 sec)

mysql> select * from tab_null where null=null;
Empty set (0.00 sec)

# SQL Server

select * from tab_null where name is null;
        ID NAME
---------- ----------
         2

select * from tab_null where name is not null;
        ID NAME
---------- ----------
         1 anbob

select * from tab_null where name=null;
no rows selected

select * from tab_null where null=null;
no rows selected

Note:
可见所有数据库的结果是一样的。

# Mysql
mysql> select 1 from tab_null where 1 not in (null);
Empty set (0.00 sec)

mysql> select 1 from tab_null where null not in (null);
Empty set (0.00 sec)

mysql> select 1 from tab_null where null  in (null);
Empty set (0.00 sec)

mysql> select 1 from tab_null where exists(select null from dual);
+---+
| 1 |
+---+
| 1 |
| 1 |
| 1 |
+---+
3 rows in set (0.05 sec)

Note:
这类在4个库返回也是一样的,上面只附了MySQL,不再展示其它库。

唯一约束

# oracle
SQL> alter table tab_null add constraint c_tab_null_name_uni  unique(name);
Table altered.

SQL> INSERT INTO tab_null VALUES(3,NULL);
1 row created.

SQL> select * from tab_null;
        ID NAME
---------- ----------
         1 anbob
         2
         3
# postgresql
postgres=# alter table tab_null add constraint c_tab_null_name_uni  unique(name);
ALTER TABLE
postgres=# INSERT INTO tab_null VALUES(3,NULL);
INSERT 0 1
postgres=# select * from tab_null;
 id |    name
----+------------
  1 | anbob
  2 |
  3 |
(3 rows)

# MySQL/MariaDB
mysql> alter table tab_null add constraint c_tab_null_name_uni  unique(name);
Query OK, 0 rows affected (0.14 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> INSERT INTO tab_null VALUES(3,NULL);
Query OK, 1 row affected (0.01 sec)

mysql> select * from tab_null;
+------+-------+
| id   | name  |
+------+-------+
|    1 | anbob |
|    2 | NULL  |
|    3 | NULL  |
+------+-------+
3 rows in set (0.00 sec)

# SQL SERVER
alter table tab_null add constraint c_tab_null_name_uni  unique(name);

INSERT INTO tab_null VALUES(3,NULL);
Msg 2627 Level 14 State 1 Line 12
Violation of UNIQUE KEY constraint 'c_tab_null_name_uni'. 
Cannot insert duplicate key in object 'dbo.tab_null'. The duplicate key value is (<NULL>).

Note:
这里只有SQL SERVER提示一个表中的null和null是重复记录, 其它库可以正常insert 多个 null 到有唯一约束的表。

NULL使用索引

# MySQL/MariaDB
mysql> alter table tab_null drop constraint c_tab_null_name_uni;
Query OK, 0 rows affected (0.05 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> create index idx_tab_null_name on tab_null(name);
Query OK, 0 rows affected (0.05 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select * from tab_null where name is null;
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table    | partitions | type | possible_keys     | key               | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | tab_null | NULL       | ref  | idx_tab_null_name | idx_tab_null_name | 41      | const |    2 |   100.00 | Using index condition |
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

# PostgreSQL
postgres=# alter table tab_null drop constraint c_tab_null_name_uni;
ALTER TABLE
postgres=# create index idx_tab_null_name on tab_null(name);
CREATE INDEX
postgres=# explain analyze select * from tab_null where name is null;
                                            QUERY PLAN
---------------------------------------------------------------------------------------------------
 Seq Scan on tab_null  (cost=0.00..1.03 rows=1 width=18) (actual time=0.008..0.009 rows=2 loops=1)
   Filter: (name IS NULL)
   Rows Removed by Filter: 1
 Planning Time: 0.130 ms
 Execution Time: 0.019 ms
-- 因为小表代价,这是使用了seq scan 全表扫,下面往表里insert一些数据

postgres=# insert into tab_null select generate_series,generate_series||'anbob' from generate_series(101,10000);
INSERT 0 9900
postgres=# analyze tab_null;
ANALYZE
postgres=# explain analyze select * from tab_null where name is null;
                                                         QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
 Index Scan using idx_tab_null_name on tab_null  (cost=0.29..9.64 rows=2 width=15) (actual time=0.010..0.011 rows=2 loops=1)
   Index Cond: (name IS NULL)
 Planning Time: 0.199 ms
 Execution Time: 0.031 ms
(4 rows)

# Oracle
SQL>alter table tab_null drop constraint c_tab_null_name_uni;
Table altered.

SQL> create index idx_tab_null_name on tab_null(name);
Index created.

SQL> explain plan for select /*+index(t)*/ * from tab_null t where name is null;

Explained.

SQL> @x2

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------
Plan hash value: 2647411751

------------------------------------------------------------------------------
| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |          |     2 |    50 |     2   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TAB_NULL |     2 |    50 |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("NAME" IS NULL)

Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1 (U - Unused (1))
---------------------------------------------------------------------------

   1 -  SEL$1 / "T"@"SEL$1"
         U -  index(t)

-- 解决这个问题可以增加常数的复合索引

SQL> create index idx_tab_null_name_0 on tab_null(name,0);
Index created.

SQL> explain plan for select /*+index(t)*/ * from tab_null t where name is null;
Explained.

SQL> @x2
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------
Plan hash value: 2247804559

-----------------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name                | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                     |     2 |    50 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| TAB_NULL            |     2 |    50 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | IDX_TAB_NULL_NAME_0 |     1 |       |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("NAME" IS NULL)

# SQL SERVER
SET statistics profile on;
SELECT * FROM tab_null with (index(idx_tab_null_name)) Where name IS NULL;

image-20230916201149290

Note:
对于is null的谓词条件注意MySQL、MariaDB即使很少的记录也使用的index感觉更像是RULE CBO;

PostgreSQL开始使用了全表扫,也可能是基于代价的估算,全表扫要优于使用索引,因为pg默认没有像oracle,sql server, mysql 自带的hint可以强制使用索引,后来我们填充了更多的数据,PostgreSQL也使用上了索引,当然PG 也有扩展pg_hint_plan可以实现,不过PG认为用户中使用hint干扰优化器是不好的习惯,不应该那么做;

Oracle数据库因为单列索引不会储存null值,所以is null 即使使用hint 也无法使用索引,需要优化小技巧,增加常量的复合索引使用索引;

SQL Server同样我们在加hint with (index(idx_tab_null_name))后,执行计划中的Index Seek 也可以确认用上了索引。

字符串拼接

# sql server 2019
select null+'anbob'

-------------
null

# mariadb 10.6
select null+'anbob'

-------------
null

# mysql 8.0
select null+'anbob'

-------------
null

# postgres 13
postgres=# select 'anbob'||null;
 ?column?
----------

(1 row)

# oracle 21c
SQL> select null||'anbob' from dual;

NULL|
-----
anbob

# OpenGauss
[og@oel7db1 data]$ gsql -d anbob -p 15432
gsql ((openGauss 2.0.0 build 78689da9) compiled at 2021-03-31 21:04:03 commit 0 last mr  )
NOTICE : The password has been expired, please change the password.
Non-SSL connection (SSL connection is recommended when requiring high-security)
Type "help" for help.
                                  ^
anbob=# select null||'anbob' ;
 ?column?
----------
 anbob
(1 row)

Note:
注意也是只有Oracle不同于其它库,在null值和其它字符串拼接后可以正常返回部分有效值,而其它所有库全部返回空。

总结:

对于null值的谓词过滤条件时(IS NULL、IS NOT NULL, >,< ,=),4个数据库返回数据记录是一样的;

对于null 值的唯一约束,只有SQL Server不允许null 记录重复,而其它数据库不限制;

对于is null使用Btree索引, 只有Oracle是默认无法索引的(需要创建常量的复合索引),其它数据库优化器在认为合适时可以正常使用索引;

对于null与字符串拼接就更有意思,sql server\postgresql\mysql在与null拼接后返回null, Oracle返回是字符串的值,而且基于Postgresql的OpenGauss返回和Oracle相同也是字符串,看来是与oracle做过兼容性修改。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1878726.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux下安装RocketMQ:从零开始的消息中间件之旅

感谢您阅读本文&#xff0c;欢迎“一键三连”。作者定会不负众望&#xff0c;按时按量创作出更优质的内容。 ❤️ 1. 毕业设计专栏&#xff0c;毕业季咱们不慌&#xff0c;上千款毕业设计等你来选。 RocketMQ是一款分布式消息中间件&#xff0c;具有高吞吐量、低延迟、高可用性…

【工具分享】SQLmap

文章目录 工具介绍安装方式环境准备安装 sqlmap 工具介绍 sqlmap 是一个非常强大的自动化 SQL 注入工具&#xff0c;主要用于渗透测试和安全审计。它能够检测和利用 SQL 注入漏洞&#xff0c;进而访问数据库服务器。 GitHub&#xff1a;https://github.com/sqlmapproject/sql…

【Vue】单向和双向数据绑定

在 Vue.js 中&#xff0c;数据绑定可以分为单向数据绑定和双向数据绑定两种类型。 单向数据绑定 单向数据绑定是指数据从模型流向视图&#xff0c;即数据的变化会自动反映到视图中&#xff0c;但视图中的变化不会自动反映回模型。Vue.js 中的单向数据绑定主要通过以下方式实现…

【数据可视化技术】1、如何使用Matplotlib和Seaborn库在Python中绘制热力图

热力图是一种数据可视化技术&#xff0c;可以显示变量之间的相关性。这个代码段是数据分析和可视化的常用方法&#xff0c;特别适合于展示变量之间的相关性&#xff0c;对于数据科学和机器学习项目非常有帮助。 1、 导入必要的库 首先&#xff0c;确保你已经安装了matplotlib…

机器学习引领教育革命:智能教育的新时代

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀目录 &#x1f4d2;1. 引言&#x1f4d9;2. 机器学习在教育中的应用&#x1f31e;个性化学习&#x1f319;评估与反馈的智能化⭐教学资源的优…

Ubuntu qemu虚拟机 NAT网络 第一次使用,VNC访问

比如Windows 7 虚拟机 要手工设置网络

什么是等级保护2.0?

等保的全称是信息安全等级保护&#xff0c;是《网络安全法》规定的必须强制执行的&#xff0c;保障公民、社会、国家利益的重要工作。 官方定义&#xff1a;等级保护是对信息和信息载体按照重要性等级分级别进行保护的一种工作&#xff0c;指对国家重要信息、法人和其他组织及公…

数据结构历年考研真题对应知识点(树的基本概念)

目录 5.1树的基本概念 5.1.2基本术语 【森林中树的数量、边数和结点数的关系&#xff08;2016&#xff09;】 5.1.3树的性质 【树中结点数和度数的关系的应用&#xff08;2010、2016&#xff09;】 【指定结点数的三叉树的最小高度分析&#xff08;2022&#xff09;】 5.1…

摸鱼大数据——Spark基础——Spark环境安装——Spark Local[*]搭建

一、虚拟机配置 查看每一台的虚拟机的IP地址和网关地址 查看路径: cat /etc/sysconfig/network-scripts/ifcfg-ens33 2.修改 VMware的网络地址: 使用VMnet8 3.修改windows的对应VMware的网卡地址 4.通过finalshell 或者其他的shell连接工具即可连接使用即可, 连接后, 测试一…

前端面试题(基础篇十三)

一、async 和 defer 的作用是什么&#xff1f;有什么区别&#xff1f; &#xff08;1&#xff09;脚本没有 defer 或 async&#xff0c;浏览器会立即加载并执行指定的脚本&#xff0c;也就是说不等待后续载入的文档元素&#xff0c;读到就加载并执行。 &#xff08;2&#xff0…

dledger原理源码分析系列(三)-选主

简介 dledger是openmessaging的一个组件&#xff0c; raft算法实现&#xff0c;用于分布式日志&#xff0c;本系列分析dledger如何实现raft概念&#xff0c;以及dledger在rocketmq的应用 本系列使用dledger v0.40 本文分析dledger的选主 关键词 Raft Openmessaging 心跳/选…

机电公司管理小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;管理员管理&#xff0c;客户管理&#xff0c;公告管理&#xff0c;考勤管理&#xff0c;请假管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;公告&#xff0c;机电零件&…

mmap()函数和munmap()函数的例子

代码&#xff1a; #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <string.h> #include <stdio.h> #include <unistd.h>#define FILELENGTH 80 int main(void) {int fd-1;char …

推荐4款好用到飞起的工具

爱发音 “爱发音”是一个专注于英语音标和字母发音学习的在线平台。该网站支持多平台访问&#xff0c;包括电脑、平板和手机&#xff0c;用户可以随时随地进行发音练习。爱发音提供美式音标、英式音标以及字母表的发音教学&#xff0c;用户可以通过点击音标来发音&#xff0c;长…

无水印视频素材库有哪些?高清无水印素材网站分享!

在这个数字化时代&#xff0c;短视频创作已成为流行趋势。为了让您的视频内容更具吸引力&#xff0c;选择合适的无水印高清视频素材至关重要。今天&#xff0c;我将向您推荐几个优秀的视频素材库&#xff0c;这些资源网站将大大提高您的创作效率和视频质感。 蛙学素材网&#…

热管的原理和棒芯的加工

当热管的一端受热时&#xff0c;毛细芯中的液体蒸发汽化&#xff0c;蒸汽在微小的压差下流向另一端&#xff0c;放出热量凝结成液体&#xff0c;液体再靠毛细力&#xff08;或重力&#xff09;的作用&#xff0c;沿多孔材料流回蒸发段。如此循环不已&#xff0c;热量便从一端传…

动态选线,动态的选择变量的位宽

一、原理 参考博客&#xff1a;&#xff1c;Verilog&#xff1e; 语法技巧&#xff1a;数据位操作_verilog移位操作-CSDN博客 下图是从作者的博客cv过来的一张图&#xff0c;讲的非常的清晰。实现了动态的选择选择数据的位宽&#xff0c;只需要动态的改变base_expr就可以。 …

数据结构算法之B树

一、绪论 1.1 数据结构的概念和作用 1.2 B树的起源和应用领域 二、B树的基本原理 2.1 B树的定义和特点 2.2 B树的结构和节点组成 2.3 B树的插入 2.4 B树的删除操作 三、B树的优势和应用 3.1 B树在数据库系统中的应用 3.2 B树在文件系统中的应用 3.3 B树在内存管理中…

ffmpeg编码图象时报错Invalid buffer size, packet size * < expected frame_size *

使用ffmpeg将单个yuv文件编码转为jpg或其他图像格式时&#xff0c;报错&#xff1a; Truncating packet of size 11985408 to 3585 [rawvideo 0x1bd5390] Packet corrupt (stream 0, dts 1). image_3264_2448_0.yuv: corrupt input packet in stream 0 [rawvideo 0x1bd7c60…

期末模拟题---期末复习3

头插法建立单链表 #include <stdio.h> #include <stdlib.h>struct Node //定义结构体 {char data; //数据域struct Node * next; //指针域 };/* 请在这里填写答案 */ struct Node * CreateList (struct Node * head) {struct Node *p;char ch;scanf(&…