首页 > 数据库 >MariaDB/MySQL的null值条件和索引

MariaDB/MySQL的null值条件和索引

时间:2023-06-13 22:23:02浏览次数:69  
标签:COUNT null idx NULL +----------+ MySQL MariaDB WHERE row

对于应用程序来说,像这样使用WHERE条件并不罕见:

WHERE status = 'DELETED' OR status IS NULL

如果运行EXPLAIN,这样的条件通常只会导致type列显示为ref_or_null。然而,如果没有NULL检查,它将显示为ref。

但是,这是否意味着执行过程中只会发生一个细小的变化,而查询仍然会非常快呢?答案是:视情况而定。这个回答很令人沮丧,但是通过下面的解释也许可以减轻这种沮丧。


创建了一个略多于350万行的测试表。按照这个顺序在列(a, b)上建立了一个索引,只在(b)上建立了另一个索引。然后运行了以下查询,运行感觉很好。

mysql> SELECT COUNT(*) FROM t WHERE (a = 2 OR a IS NULL) AND (b = 5);
+----------+
| COUNT(*) |
+----------+
|      212 |
+----------+
1 row in set (0.01 sec)

mysql> SELECT COUNT(*) FROM t WHERE (a = 2) AND (b = 5 OR b IS NULL);
+----------+     
| COUNT(*) |
+----------+
|      120 |
+----------+
1 row in set (0.01 sec)

可以看到,如果对索引中的任一列上添加一个is null条件,查询仍然很快。问题是,如果在多个列上使用is null呢?

mysql> SELECT COUNT(*) FROM t WHERE (a = 2 OR a IS NULL) AND (b =  5 OR b IS NULL);
+----------+
| COUNT(*) |
+----------+
|  1466664 |
+----------+
1 row in set (1 min 21.32 sec)

太慢了!你可能认为这取决于行数。选择了更多的行,也许这就是慢的原因。我们可以很容易地测试它:我运行UPDATE将所有null替换为0值,并重复查询:

mysql> SELECT COUNT(*) FROM t WHERE (a = 2 OR a IS NULL) AND (b =  5 OR b IS NULL);
+----------+
| COUNT(*) |
+----------+
|      120 |
+----------+
1 row in set (0.02 sec)

非常快!但是,这种变慢真的正常吗?让我们看看如果我们查0值会发生什么:

mysql> SELECT COUNT(*) FROM t WHERE (a = 2 OR a = 0) AND (b =  5 OR b = 0);
+----------+
| COUNT(*) |
+----------+
|  2457536 |
+----------+
1 row in set (1.93 sec)

这次选择了更多的行,但是查询所用的时间不到2秒。仍然很慢,但这是一个巨大的进步:之前的版本需要81秒!

查询优化器
这两个查询之间的差异是什么呢?
第一个查询不能正确的使用索引,因为ref_or_null不能用在多个列上。采取的操作是在b上使用了索引:

mysql> EXPLAIN SELECT COUNT(*) FROM t WHERE (a = 2 OR a IS NULL) AND (b =  5 OR b IS NULL) G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t
   partitions: NULL
         type: ref_or_null
possible_keys: idx_b,idx_a_b
          key: idx_b
      key_len: 10
          ref: NULL
         rows: 1815359
     filtered: 11.43
        Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

第二个查询是在每个列上查询常规的值,因此在正确的索引上使用了range检索:

mysql> EXPLAIN SELECT COUNT(*) FROM t WHERE (a = 2 OR a = 0) AND (b =  5 OR b = 0) G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t
   partitions: NULL
         type: range
possible_keys: idx_b,idx_a_b
          key: idx_a_b
      key_len: 10
          ref: NULL
         rows: 1908763
     filtered: 100.00
        Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

range检索可以涉及来自同一索引的多个列。Extra列确认仅通过读取idx_a_b索引来执行查询。

有关此优化的更多细节,请参阅MySQL文档中的IS NULL优化页面。


解决方案是使用常规值而不是NULL。逻辑上的意思是"没有",但它不是NULL。通常情况下,用0表示,或者空字符串" ",或者UNIX纪元的开始'1970-01-01 00:00:00'。

你可能对查询优化的这一方面不熟悉,但是可能至少会高兴地知道"=优先与>"的优化规则适用于以下两个查询。换句话说,它不受ref_or_null限制的影响。

mysql> EXPLAIN SELECT COUNT(*) FROM t WHERE (a = 2000 OR a = 0) AND (b >  5 OR b = 0) G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t
   partitions: NULL
         type: range
possible_keys: idx_b,idx_a_b
          key: idx_a_b
      key_len: 10
          ref: NULL
         rows: 1761559
     filtered: 100.00
        Extra: Using where; Using index
1 row in set, 1 warning (0.01 sec)

mysql> EXPLAIN SELECT COUNT(*) FROM t WHERE (a = 2000 OR a IS NULL) AND (b >  5 OR b IS NULL) G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t
   partitions: NULL
         type: range
possible_keys: idx_b,idx_a_b
          key: idx_a_b
      key_len: 10
          ref: NULL
         rows: 4
     filtered: 100.00
        Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

MySQL对NULL值可以使用索引、range检索优化。

标签:COUNT,null,idx,NULL,+----------+,MySQL,MariaDB,WHERE,row
From: https://www.cnblogs.com/abclife/p/17478289.html

相关文章

  • MySQL索引
    一:索引的声明及使用索引是存储引擎用于快速找到记录的一种数据结构。索引对于良好的性能非常关键,尤其是当表中的数据量越来越大时,索引对性能的影响就愈发重要。索引优化应该是对查询性能优化最有效的手段,创建一个真正最优的索引经常需要重写SQL查询语句。要理解MySQL中索引......
  • 记录一个MySQL中order by 和 limit 连用导致分页查询不生效的坑
    具体现象和这位同学的一致,具体的解决办法也是参考这位同学的做法参考文章地址:https://www.cnblogs.com/yuluoxingkong/p/10681583.html......
  • mysql 8.0.32 视图中文无法过滤
    mysql8.0.32视图中采用unionall或union合并获取结果时,含有中文的字段过滤失效会提示类似如下的信息Warning|1300|Cannotconvertstring'\xE5\xAE\xA2\xE6\x88\xB7...'fromutf8mb4tobinary这是此版本bug.在对派生表使用where条件时,如果对应值超过128(ascii值),对......
  • mysql一些小知识点
    mysql的三值逻辑mysql使用的是三值逻辑:TRUE FALSE UNKNOWN。任何与null值进行的比较都会与第三种值UNKNOWN做比较。这个“任何值”包括null本身。所以mysql提供了isnull和isnotnull两种操作来对null做特殊判断因此,在进行select查询时,如果查询到的值有为空的时候......
  • 工作记录_mysql_AND优先级高于OR优先级
    1.错误示例SELECT t.task_department_name, COUNT(*)total_count, SUM(CASEWHENstatus='done'THEN1ELSE0END)ASfinish_count, SUM(CASEWHENstatus<>'done'THEN1ELSE0END)ASunfinish_countFROM`t_task`t--WHEREtask_typ......
  • MySQL字符索引没用上问题
    某一天,接口突然502,运维同学说没有可用的PHP进程了,看监控说是这个接口夯住了,导致请求进不来,临时把这个接口给返回了200(PS:线上这个接口没有实际作用,所以这么操作了);给了慢查询的SQL,用explain看了下,发现竟然没有用到创建的索引,此时数据库的量有大概150万行,对SQL里where字段加了双引......
  • 记录一段mysql代码
    SELECTf.*,tmp.userid,tmp.cishuFROMfx_userf,(SELECTa.id,b.useridASuserid,COUNT(*)AScishuFROM`fx_user`ASaLEFTJOIN`fx_plan`ASbONa.id=b.useridANDb.fxtype=0GROUPBYa.id)tmpWHEREf.id=tmp.idANDpriority=1ORDERBYid......
  • C#连接MySql数据库的方法
     用MySQLDriverCS连接MySQL数据库   先下载和安装MySQLDriverCS,地址:   http://sourceforge.net/projects/mysqldrivercs/   在安装文件夹下面找到MySQLDriver.dll,然后将MySQLDriver.dll添加引用到项目中   注:我下载的是版本是MySQLDriverCS-n-EasyQueryTools-4.0......
  • mysql mariadb修改端口后启动失败
    修改端口号启动失败关闭SETLinux临时关闭,不需要重启电脑,重启后失效执行命令:setenforce0永久关闭,需要重启机器,修改/etc/selinux/config文件将SELINUX=enforcing改为SELINUX=disabled重启mysql服务即可......
  • ios开发 :CUICatalog: Invalid asset name supplied: '(null)'
    _iconImage.image=[UIImageimageNamed:sourceDic[@“image”]];明明有图片,但还是提示这个图片名称不存在报错CUICatalog:Invalidassetnamesupplied:'(null)'打断点进去,显示_iconImage的值是nil添加断点定位到错误在109行-(NSArray*)messageTableSource{if(_mes......