Oracle、MySQL、PostGreSQL、SQL Server-空值

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
---------- ----------2SQL> select * from tab_null where name is not null;ID NAME
---------- ----------1 anbobSQL> select * from tab_null where name=null;
no rows selectedSQL> 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 Serverselect * from tab_null where name is null;ID NAME
---------- ----------2select * from tab_null where name is not null;ID NAME
---------- ----------1 anbobselect * from tab_null where name=null;
no rows selectedselect * 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 anbob23
# 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 | anbob2 |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: 0mysql> 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: 0mysql> create index idx_tab_null_name on tab_null(name);
Query OK, 0 rows affected (0.05 sec)
Records: 0  Duplicates: 0  Warnings: 0mysql> 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: 1Planning Time: 0.130 msExecution 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 msExecution 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> @x2PLAN_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.rhkb.cn/news/364540.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024年6月26日 (周三) 叶子游戏新闻

老板键工具来唤去: 它可以为常用程序自定义快捷键&#xff0c;实现一键唤起、一键隐藏的 Windows 工具&#xff0c;并且支持窗口动态绑定快捷键&#xff08;无需设置自动实现&#xff09;。 土豆录屏: 免费、无录制时长限制、无水印的录屏软件 《Granblue Fantasy Versus: Risi…

Cisco Identity Services Engine (ISE) 3.3 Patch 2 - 基于身份的网络访问控制和策略实施系统

Cisco Identity Services Engine (ISE) 3.3 Patch 2 - 基于身份的网络访问控制和策略实施系统 思科身份服务引擎 (ISE) - 下一代 NAC 解决方案 请访问原文链接&#xff1a;Cisco Identity Services Engine (ISE) 3.3 Patch 2 - 基于身份的网络访问控制和策略实施系统&#xf…

笔灵AI写作:释放创意,提升写作效率的秘诀

内容为王&#xff0c;在内容创作的世界中尤为重要。然而&#xff0c;面对写作时常常感到无从下手&#xff1a;有时缺乏灵感&#xff0c;有时难以表达清楚自己的想法。AI写作助手的出现&#xff0c;为这些问题提供了创新的解决方案&#xff0c;极大地改变了内容创作的过程。 今…

STM32——使用TIM输出比较产生PWM波形控制舵机转角

一、输出比较简介&#xff1a; 只有高级定时器和通用寄存器才有输入捕获/输出比较电路&#xff0c;他们有四个CCR&#xff08;捕获/比较寄存器&#xff09;&#xff0c;共用一个CNT&#xff08;计数器&#xff09;&#xff0c;而输出比较功能是用来输出PWM波形的。 红圈部分…

C语言调用python

1、linux中安装libpython3.10-dev 通过C语言调用python代码&#xff0c;需要先安装libpython3的dev依赖库&#xff08;不同的ubuntu版本下&#xff0c;python版本可能会有差异&#xff0c; 比如ubuntu 22.04里是libpython3.10-dev&#xff09;。 首先可以通过以下命令验证…

数字信号处理实验一(离散信号及离散系统的MATLAB编程实现)

实验要求&#xff1a; 离散信号及离散系统的MATLAB编程实现&#xff08;2学时&#xff09; 要求&#xff1a; 编写一程序&#xff0c;输出一定长度&#xff08;点数&#xff09;&#xff0c;具有一定幅度、&#xff08;角&#xff09;频率和初始相位的实&#xff08;或复&…

gitee配置ssh教程

生成公钥 执行命令&#xff1a; ssh-keygen -t rsa查看公钥 cat ~/.ssh/id_rsa.pub这个公钥就是要复制粘贴到Gitee中的ssh公钥。 配置Gitee SSH公钥 来到Gitee的ssh公钥中&#xff0c;配置

ONLYOFFICE8.1版本桌面编辑器简单测评

ONLYOFFICE官网链接&#xff1a;在线PDF查看器和转换器 | ONLYOFFICE ONLYOFFICE介绍&#xff1a;https://www.onlyoffice.com/zh/office-suite.aspx OnlyOffice 是一款免费且开源的 Office 协作办公套件&#xff0c;支持桌面端和移动端等多平台&#xff0c;由一家领先的 IT 公…

热管的原理和棒芯的加工

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

禁止浏览器对input的自动填充和填充提示(适用于谷歌、火狐、Edge(原IE浏览器)等常见浏览器)

目录 1.要解决的问题2.一技能&#xff1a;原生属性&#xff0c;小试牛刀3.二技能&#xff1a;傀儡input&#xff0c;瞒天过海4.三技能&#xff1a;JavaScript出击&#xff0c;直接开大5.九九八十一难&#xff0c;永远还有最后一难 写在前面&#xff1a; 如有转载&#xff0c;务…

labview排错

源代码正常跑&#xff0c;应用程序报这个错&#xff0c;是因为源代码的可以找到项目路径内所有dll的路径&#xff0c;而应用程序只能找到data文件夹的dll文件 解决查看源代码中.net的程序集的路径&#xff0c;复制对应的dll到data文件夹下 在执行developinterface.dll出现labv…

【简单讲解下Fine-tuning BERT,什么是Fine-tuning BERT?】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

线性代数--行列式1

本篇来自对线性代数第一篇的行列式的一个总结。 主要是行列式中有些关键点和注意事项&#xff0c;便于之后的考研复习使用。 首先&#xff0c;对于普通的二阶和三阶行列式&#xff0c;我们可以直接对其进行拆开&#xff0c;展开。 而对于n阶行列式 其行列式的值等于它的任意…

ModuleNotFoundError: No module named ‘_sysconfigdata_x86_64_conda_linux_gnu‘

ModuleNotFoundError: No module named _sysconfigdata_x86_64_conda_linux_gnu 1.软件环境⚙️2.问题描述&#x1f50d;3.解决方法&#x1f421;4.结果预览&#x1f914; 1.软件环境⚙️ Ubuntu 20.04 Python 3.7.0 2.问题描述&#x1f50d; 今天发现更新conda之后&#xff0…

用pycharm进行python爬虫的步骤

使用 pycharm 进行 python 爬虫的步骤&#xff1a;下载并安装 pycharm。创建一个新项目。安装 requests 和 beautifulsoup 库。编写爬虫脚本&#xff0c;包括获取页面内容、解析 html 和提取数据的代码。运行爬虫脚本。保存和处理提取到的数据。 用 PyCharm 进行 Python 爬虫的…

代码随想录-Day43

52. 携带研究材料&#xff08;第七期模拟笔试&#xff09; 小明是一位科学家&#xff0c;他需要参加一场重要的国际科学大会&#xff0c;以展示自己的最新研究成果。他需要带一些研究材料&#xff0c;但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等…

基于MongoDB的电影影评分析

项目源码及资料 项目介绍 1、从豆瓣网爬取Top10的电影数据 爬取网址: https://movie.douban.com/top250 1.1 爬取Top10的影视信息 mv_data [] i 0 for x in soup.select(.item):i 1mv_name re.search(>([^<])<, str(x.select(.info > .hd > a > .tit…

Flink 从入门到放弃

0 写在前面 程序员闯荡江湖的一生都在与数据打交道&#xff0c;初入江湖时基于 MySQL 的 CRUD&#xff0c;渐入佳境后利用 Redis 实现查询加速及分布式控制&#xff0c;本质上都是数据处理&#xff1b;无论主动/被动&#xff0c;都在利用数据来达成业务/技术目的。自然而然的&a…

java基于ssm+jsp 多用户博客个人网站

1管理员功能模块 管理员登录&#xff0c;管理员通过输入用户名、密码等信息进行系统登录&#xff0c;如图1所示。 图1管理员登录界面图 管理员登录进入个人网站可以查看&#xff1b;个人中心、博文类型管理、学生博客管理、学生管理、论坛信息、管理员管理、我的收藏管理、留…

CriticGPT: 用 GPT-4 找出 GPT-4 的错误

CriticGPT 是 OpenAI 发布的一个基于 GPT-4 的模型&#xff0c;它可以帮助我们人类 Review 并纠正 ChatGPT 在生成代码时的错误。使用 CriticGPT 审查代码时&#xff0c;有 60% 的概率生成的代码更好更正确。