【MySQL】 IS NOT NULL 和 != NULL 的区别?
作者:mmseoamin日期:2023-12-25

背景

最近在开发小伙伴的需求,遇到了一个数据库统计的问题,

is not null 结果正确

!=null 结果就不对,然后就激发了获取真理的想法,那必须的查查

咋回事嘞?

【MySQL】 IS NOT NULL 和 != NULL 的区别?,在这里插入图片描述,第1张

开整

在用MySQL的过程中,你是否存在过如下的几个疑问?

  • 我的字段类型明明指定的是NOT NULL,但是为什么还是可以插入空值呢?
  • 为什么NOT NULL的效率比NULL更高?
  • 在查询空字段的记录时是用"select * from table where col <> ‘’ “还是用"select * from table where col is not null”?

    带着疑问,我们来看看NOT NULL和NULL有什么不一样呢?要搞清楚这两个的区别。首先,我们先要理解"空值"和"NULL"的含义:

    • 空值是不占用空间的。
    • NULL是会占用空间的,我们来看看官方对这个NULL的描述。MySQL的官方描述如下:

      “NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.”

      怎么理解呢?简单举个栗子:

      假设有一个瓶子,空值表示的是瓶子里什么都没有,NULL表示的是瓶子里面状态的是空气。可以理解为什么NULL也会占用空间了吧。

      下面来通过一个实例例子来测试一下。首先建一个表,表引擎使用InnoDB,建表语句如下:

      create table test(
        c1 varchar(10) not null, 
        c2 varchar(10) default null
      ) engine = InnoDB;
      

      验证插入数据和查询:

      mysql> insert into test(c1, c2) values(null, 0);
      ERROR 1048 (23000): Column 'c1' cannot be null
      mysql> insert into test(c1, c2) values('', 0);
      Query OK, 1 row affected (0.00 sec)
      # null字符串
      mysql> insert into test(c1, c2) values('null', 0);
      Query OK, 1 row affected (0.00 sec)
      mysql> insert into test(c1, c2) values('', null);
      Query OK, 1 row affected (0.00 sec)
      mysql> select * from test;
      +------+------+
      | c1   | c2   |
      +------+------+
      |      | 0    |
      | null | 0    |
      |      | NULL |
      +------+------+
      3 rows in set (0.00 sec)
      

      从上述结果中可以看到,NOT NULL的字段是没办法插入NULL值的,只能插入空值’'。上面第三个SQL插入的NULL是个字符串格式的NULL,对于MyISAM的存储引擎,测试的结果和上述结果是一样的。所以第1个疑问也就了解了吧。

      对于第2个问题,因为NULL值是占了一定空间的,所以在MySQL进行字段比较的时候,值为NULL的字段也是会参与比较的,所以是会对性能有一定的影响。

      当字段上包含有索引时,由于B树索引是不会存储NULL值的,所以在使用这个字段做为查询条件时,对性能的影响还是比较大的,在平时创建索引的时候,应该尽量保证列的值不为NULL。

      针对上述的结论,有几个针对NULL和NOT NULL的常见优化建议:

      • MySQL如果不指定列的约束,默认就是允许NULL,TIMESTAMP类型的字段除外。所以在非必要情况下,尽量设置列的约束为NOT NULL。
      • 如果列的值为NULL,通过这个为NULL的列进行条件查询时,MySQL更难做优化,因为为NULL的列会让索引的统计和值的比较更加复杂。
      • 如果计划在某个列上创建索引,那么需要尽量避免这个列中的字段值为NULL。在优化的过程中,把NULL改为NOT NULL对性能的提升并不是很明显。所以如果在使用过程中没有问题的话,没有必要首先去做NULL到NOT NULL的优化。

        来通过实际例子看看最后一个问题。假如需要查询上述test表中c1不为空的所有数据,应该使用"<> ‘’"呢?还是使用"IS NOT NULL"呢?测试结果如下:

        mysql> select * from test where c1 is not null;
        +------+------+
        | c1   | c2   |
        +------+------+
        |      | 1    |
        | null | 1    |
        |      | NULL |
        +------+------+
        3 rows in set (0.00 sec)
        mysql> select * from test where c1 <> '';
        +------+------+
        | c1   | c2   |
        +------+------+
        | null | 1    |
        +------+------+
        1 row in set (0.00 sec)
        

        可以看到,不同的查询条件,对于查询的结果区别还是特别大的。所以在使用过程中需要根据业务场景,选择不同的查询条件。

        附录

        附录1:MySQL索引失效的常见情况
        1. 最左前缀原则。例如:存在联合索引idx_a_b(a, b),查询条件使用where b = 1则无法使用索引
        2. LIKE 前置模糊查询。例如:col_name like '%test' 或 col_name like '%test%'
        3. 索引列使用函数或存在计算。例如:存在索引idx_col(col),查询条件使用where left(col, 2) = 'te'
        4. 查询条件使用 is not null。设计表结构时尽量设置 not null约束
        5. 字段类型出现隐式转换。例如:存在字段test_col为varchar类型,查询时使用了where test_col = 1,隐式转为了int类型导致索引失效
        6. 条件中有 or 存在可能不会使用索引。例如:查询条件为where a = 'testa' or b = 'testb',存在索引idx_a(a),此时也不会使用索引,除非为b字段也添加索引
        7. 查询结果超过整体结果的25%或三分之一,或者表数据量比较少时,MySQL认为全表扫描代价更小,会导致索引失效
        附录2:MySQL关键字列表

        R 表示为 MySQL 预留关键字

        关键字关键字关键字
        ACCESSIBLE(R)ACCOUNTACTION
        ADD(R)AFTERAGAINST
        AGGREGATEALGORITHMALL(R)
        ALTER(R)ALWAYSANALYSE
        ANALYZE(R)AND(R)ANY
        AS(R)ASC(R)ASCII
        ASENSITIVE(R)ATAUTOEXTEND_SIZE
        AUTO_INCREMENTAVGAVG_ROW_LENGTH
        BACKUPBEFORE(R)BEGIN
        BETWEEN(R)BIGINT(R)BINARY(R)
        BINLOGBITBLOB(R)
        BLOCKBOOLBOOLEAN
        BOTH(R)BTREEBY(R)
        BYTECACHECALL ®
        CASCADE ®CASCADEDCASE ®
        CATALOG_NAMECHAINCHANGE ®
        CHANGEDCHANNELCHAR ®
        CHARACTER ®CHARSETCHECK ®
        CHECKSUMCIPHERCLASS_ORIGIN
        CLIENTCLOSECOALESCE
        CODECOLLATE ®COLLATION
        COLUMN ®COLUMNSCOLUMN_FORMAT
        COLUMN_NAMECOMMENTCOMMIT
        COMMITTEDCOMPACTCOMPLETION
        COMPRESSEDCOMPRESSIONCONCURRENT
        CONDITION ®CONNECTIONCONSISTENT
        CONSTRAINT ®CONSTRAINT_CATALOGCONSTRAINT_NAME
        CONSTRAINT_SCHEMACONTAINSCONTEXT
        CONTINUE ®CONVERT ®CPU
        CREATE ®CROSS ®CUBE
        CURRENTCURRENT_DATE ®CURRENT_TIME ®
        CURRENT_TIMESTAMP ®CURRENT_USER ®CURSOR ®
        CURSOR_NAMEDATADATABASE ®
        DATABASES ®DATAFILEDATE
        DATETIMEDAYDAY_HOUR ®
        DAY_MICROSECOND ®DAY_MINUTE ®DAY_SECOND ®
        DEALLOCATEDEC ®DECIMAL ®
        DECLARE ®DEFAULT ®DEFAULT_AUTH
        DEFINERDELAYED ®DELAY_KEY_WRITE
        DELETE ®DESC ®DESCRIBE ®
        DES_KEY_FILEDETERMINISTIC ®DIAGNOSTICS
        DIRECTORYDISABLEDISCARD
        DISKDISTINCT ®DISTINCTROW ®
        DIV ®DODOUBLE ®
        DROP ®DUAL ®DUMPFILE
        DUPLICATEDYNAMICEACH ®
        ELSE ®ELSEIF ®ENABLE
        ENCLOSED ®ENCRYPTIONEND
        ENDSENGINEENGINES
        ENUMERRORERRORS
        ESCAPEESCAPED ®EVENT
        EVENTSEVERYEXCHANGE
        EXECUTEEXISTS ®EXIT ®
        EXPANSIONEXPIREEXPLAIN ®
        EXPORTEXTENDEDEXTENT_SIZE
        FALSE ®FASTFAULTS
        FETCH ®FIELDSFILE
        FILE_BLOCK_SIZEFILTERFIRST
        FIXEDFLOAT(R)FLOAT4(R)
        FLOAT8(R)FLUSHFOLLOWS
        FOR(R)FORCE(R)FOREIGN(R)
        FORMATFOUNDFROM(R)
        FULLFULLTEXT(R)FUNCTION
        GENERALGENERATED(R)GEOMETRY
        GEOMETRYCOLLECTIONGET(R)GET_FORMAT
        GLOBALGRANT(R)GRANTS
        GROUP(R)GROUP_REPLICATIONHANDLER
        HASHHAVING(R)HELP
        HIGH_PRIORITY(R)HOSTHOSTS
        HOURHOUR_MICROSECOND(R)HOUR_MINUTE(R)
        HOUR_SECOND(R)IDENTIFIEDIF(R)
        IGNORE(R)IGNORE_SERVER_IDSIMPORT
        IN(R)INDEX(R)INDEXES
        INFILE(R)INITIAL_SIZEINNER(R)
        INOUT(R)INSENSITIVE(R)INSERT(R)
        INSERT_METHODINSTALLINSTANCE
        INT(R)INT1(R)INT2(R)
        INT3(R)INT4(R)INT8(R)
        INTEGER(R)INTERVAL(R)INTO(R)
        INVOKERIOIO_AFTER_GTIDS(R)
        IO_BEFORE_GTIDS(R)IO_THREADIPC
        IS(R)ISOLATIONISSUER
        ITERATE(R)JOIN(R)JSON
        KEY(R)KEYS(R)KEY_BLOCK_SIZE
        KILL(R)LANGUAGELAST
        LEADING(R)LEAVE ®LEAVES
        LEFT ®LESSLEVEL
        LIKE ®LIMIT ®LINEAR(R)
        LINES(R)LINESTRINGLIST
        LOAD(R)LOCALLOCALTIME(R)
        LOCALTIMESTAMP(R)LOCK(R)LOCKS
        LOGFILELOGSLONG(R)
        LONGBLOB(R)LONGTEXT(R)LOOP(R)
        LOW_PRIORITY(R)MASTERMASTER_AUTO_POSITION
        MASTER_BIND(R)MASTER_CONNECT_RETRYMASTER_DELAY
        MASTER_HEARTBEAT_PERIODMASTER_HOSTMASTER_LOG_FILE
        MASTER_LOG_POSMASTER_PASSWORDMASTER_PORT
        MASTER_RETRY_COUNTMASTER_SERVER_IDMASTER_SSL
        MASTER_SSL_CAMASTER_SSL_CAPATHMASTER_SSL_CERT
        MASTER_SSL_CIPHERMASTER_SSL_CRLMASTER_SSL_CRLPATH
        MASTER_SSL_KEYMASTER_SSL_VERIFY_SERVER_CERT(R)MASTER_TLS_VERSION
        MASTER_USERMATCH(R)MAXVALUE(R)
        MAX_CONNECTIONS_PER_HOURMAX_QUERIES_PER_HOURMAX_ROWS
        MAX_SIZEMAX_STATEMENT_TIMEMAX_UPDATES_PER_HOUR
        MAX_USER_CONNECTIONSMEDIUMMEDIUMBLOB(R)
        MEDIUMINT(R)MEDIUMTEXT(R)MEMORY
        MERGEMESSAGE_TEXTMICROSECOND
        MIDDLEINT(R)MIGRATEMINUTE
        MINUTE_MICROSECOND(R)MINUTE_SECOND(R)MIN_ROWS
        MOD(R)MODEMODIFIES(R)
        MODIFYMONTHMULTILINESTRING
        MULTIPOINTMULTIPOLYGONMUTEX
        MYSQL_ERRNONAMENAMES
        NATIONALNATURAL(R)NCHAR
        NDBNDBCLUSTERNEVER
        NEWNEXTNO
        NODEGROUPNONBLOCKINGNONE
        NOT(R)NO_WAITNO_WRITE_TO_BINLOG(R)
        NULL(R)NUMBERNUMERIC(R)
        NVARCHAROFFSETOLD_PASSWORD
        ON(R)ONEONLY
        OPENOPTIMIZE(R)OPTIMIZER_COSTS(R)
        OPTION(R)OPTIONALLY(R)OPTIONS
        OR(R)ORDER(R)OUT(R)
        OUTER(R)OUTFILE(R)OWNER
        PACK_KEYSPAGEPARSER
        PARSE_GCOL_EXPRPARTIALPARTITION(R)
        PARTITIONINGPARTITIONSPASSWORD
        PHASEPLUGINPLUGINS
        PLUGIN_DIRPOINTPOLYGON
        PORTPRECEDESPRECISION(R)
        PREPAREPRESERVEPREV
        PRIMARY(R)PRIVILEGESPROCEDURE(R)
        PROCESSLISTPROFILEPROFILES
        PROXYPURGE(R)QUARTER
        QUERYQUICKRANGE(R)
        READ(R)READS(R)READ_ONLY
        READ_WRITE(R)REAL(R)REBUILD
        RECOVERREDOFILEREDO_BUFFER_SIZE
        REDUNDANTREFERENCES(R)REGEXP(R)
        RELAYRELAYLOGRELAY_LOG_FILE
        RELAY_LOG_POSRELAY_THREADRELEASE(R)
        RELOADREMOVERENAME(R)
        REORGANIZEREPAIRREPEAT(R)
        REPEATABLEREPLACE(R)REPLICATE_DO_DB
        REPLICATE_DO_TABLEREPLICATE_IGNORE_DBREPLICATE_IGNORE_TABLE
        REPLICATE_REWRITE_DBREPLICATE_WILD_DO_TABLEREPLICATE_WILD_IGNORE_TABLE
        REPLICATIONREQUIRE(R)RESET
        RESIGNAL(R)RESTORERESTRICT(R)
        RESUMERETURN(R)RETURNED_SQLSTATE
        RETURNSREVERSEREVOKE(R)
        RIGHT(R)RLIKE(R)ROLLBACK
        ROLLUPROTATEROUTINE
        ROWROWSROW_COUNT
        ROW_FORMATRTREESAVEPOINT
        SCHEDULESCHEMA(R)SCHEMAS(R)
        SCHEMA_NAMESECONDSECOND_MICROSECOND(R)
        SECURITYSELECT(R)SENSITIVE(R)
        SEPARATOR(R)SERIALSERIALIZABLE
        SERVERSESSIONSET(R)
        SHARESHOW(R)SHUTDOWN
        SIGNAL(R)SIGNEDSIMPLE
        SLAVESLOWSMALLINT(R)
        SNAPSHOTSOCKETSOME
        SONAMESOUNDSSOURCE
        SPATIAL ®SPECIFIC ®SQL ®
        SQLEXCEPTION ®SQLSTATE ®SQLWARNING ®
        SQL_AFTER_GTIDSSQL_AFTER_MTS_GAPSSQL_BEFORE_GTIDS
        SQL_BIG_RESULT(R)SQL_BUFFER_RESULTSQL_CACHE
        SQL_CALC_FOUND_ROWS(R)SQL_NO_CACHESQL_SMALL_RESULT(R)
        SQL_THREADSQL_TSI_DAYSQL_TSI_HOUR
        SQL_TSI_MINUTESQL_TSI_MONTHSQL_TSI_QUARTER
        SQL_TSI_SECONDSQL_TSI_WEEKSQL_TSI_YEAR
        SSL(R)STACKEDSTART
        STARTING(R)STARTSSTATS_AUTO_RECALC
        STATS_PERSISTENTSTATS_SAMPLE_PAGESSTATUS
        STOPSTORAGESTORED(R)
        STRAIGHT_JOIN(R)STRINGSUBCLASS_ORIGIN
        SUBJECTSUBPARTITIONSUBPARTITIONS
        SUPERSUSPENDSWAPS
        SWITCHESTABLE(R)TABLES
        TABLESPACETABLE_CHECKSUMTABLE_NAME
        TEMPORARYTEMPTABLETERMINATED(R)
        TEXTTHANTHEN(R)
        TIMETIMESTAMPTIMESTAMPADD
        TIMESTAMPDIFFTINYBLOB(R)TINYINT(R)
        TINYTEXT(R)TO(R)TRAILING(R)
        TRANSACTIONTRIGGER ®TRIGGERS
        TRUE ®TRUNCATETYPE
        TYPESUNCOMMITTEDUNDEFINED
        UNDO(R)UNDOFILEUNDO_BUFFER_SIZE
        UNICODEUNINSTALLUNION(R)
        UNIQUE(R)UNKNOWNUNLOCK(R)
        UNSIGNED(R)UNTILUPDATE(R)
        UPGRADEUSAGE(R)USE(R)
        USERUSER_RESOURCESUSE_FRM
        USING(R)UTC_DATE(R)UTC_TIME(R)
        UTC_TIMESTAMP(R)VALIDATIONVALUE
        VALUES(R)VARBINARY(R)VARCHAR(R)
        VARCHARACTER(R)VARIABLESVARYING(R)
        VIEWVIRTUAL(R)WAIT
        WARNINGSWEEKWEIGHT_STRING
        WHEN(R)WHERE(R)WHILE(R)
        WITH(R)WITHOUTWORK
        WRAPPERWRITE(R)X509
        XAXIDXML
        XOR(R)YEARYEAR_MONTH(R)
        ZEROFILL(R)