用于存储布尔值的 MySQL 数据类型
Posted
技术标签:
【中文标题】用于存储布尔值的 MySQL 数据类型【英文标题】:Which MySQL data type to use for storing boolean values 【发布时间】:2010-09-22 07:53:36 【问题描述】:由于 mysql 似乎没有任何“布尔”数据类型,您会“滥用”哪种数据类型在 MySQL 中存储真/假信息?
尤其是在从/向 php 脚本写入和读取的上下文中。
随着时间的推移,我已经使用并看到了几种方法:
tinyint、varchar 字段包含值 0/1, varchar 字段包含字符串 '0'/'1' 或 'true'/'false' 最后枚举包含“true”/“false”两个选项的字段。以上都不是最佳的。我更喜欢 tinyint 0/1 变体,因为 PHP 中的自动类型转换给我的布尔值相当简单。
那么您使用哪种数据类型?有没有为我忽略的布尔值设计的类型?您看到使用一种或另一种类型有什么优点/缺点吗?
【问题讨论】:
任何正在阅读此问题旧答案的人都需要了解 MySQL 在版本 5 中添加了位数据类型。尽可能使用该信息。 dev.mysql.com/doc/refman/5.0/en/bit-type.html 与Alternative to lots of booleans in MySQL?相关的问题 对于当前版本的 MYSQL 布尔类型可用-dev.mysql.com/doc/refman/5.5/en/numeric-type-overview.html 勾选此项。根据那个值零被认为是假的bit(1)
在 Excel 中导入有点**。切换到tinyint(1)
有效。
现在我们在 5 年后有了布尔值
【参考方案1】:
BOOL
和 BOOLEAN
是 TINYINT(1)
的同义词。零是false
,其他都是true
。更多信息here.
【讨论】:
(1)
只是确定值的显示方式,如果您意识到存储大小,那么您想改用BIT
@JamesHalsall:实际上,BIT(1)
和 TINYINT(1)
都将使用一个字节的存储空间。在 MySQL 5.0.3 之前,BIT
实际上是 TINYINT
的同义词。更高版本的 MySQL 更改了 BIT 的实现。但即使实现更改,BIT
数据类型仍然没有“存储大小”优势(至少对于 InnoDB 和 MyISAM;其他存储引擎,例如 NDB 可能对多个 BIT 列声明进行一些存储优化。)更大的问题是某些客户端库无法识别或正确处理返回的BIT
数据类型列。 TINYINT
效果更好。
MySQL 5.0 手册清楚地表明布尔值是 1 或 0。短语“任何其他都是 true
”是不正确的。
@Walter:这实际上是真的,解释有点缺乏。简而言之,在布尔上下文中,表达式可以计算为 NULL、FALSE 或 TRUE。在 MySQL 语句中,在布尔上下文中评估的表达式首先被评估为整数(十进制和浮点值被四舍五入,字符串以 MySQL 将字符串转换为整数的通常古怪方式转换)。 NULL 显然是 NULL(既不是 TRUE 也不是 FALSE)。整数值 0 被视为 FALSE,任何 个其他整数值(1、2、-7 等)的计算结果为 TRUE。为了兼容性,我们模仿 TINYINT 布尔值的逻辑/处理
@Walter:这很容易测试,例如SELECT 'foo' AS bar FROM dual WHERE -7
。表达式 -7 在布尔上下文中计算,查询返回一行。我们可以使用 0 或任何计算结果为整数值 0 的表达式进行测试,并且不返回任何行。如果 WHERE 子句中的表达式计算为除零以外的任何非空整数值,则表达式为 TRUE。 (我相信十进制和浮点值会“四舍五入”为整数,例如 WHERE 1/3
的计算结果为 WHERE 0
。我们使用 WHERE 'foo'
得到相同的结果,因为字符串 'foo'
的计算结果也为整数值 0。【参考方案2】:
这个问题已经得到解答,但我想我会投入 0.02 美元。
我经常使用CHAR(0)
,其中'' == true and NULL == false
。
来自MySQL docs:
CHAR(0)
在您需要一个只能占用的列时也很不错 两个值:定义为CHAR(0)
NULL
的列只占用一个 位,只能取值NULL
和''
(空字符串)。
【讨论】:
mm,如果你是我,这似乎是在自找麻烦。我的意思是,根据语言的不同,很容易发现 NULL 和 '' 之间的差异(例如 PHP)。 在节省空间(用于表示布尔值的字节数)方面,这种方法显然是赢家。这在 TINYINT 上节省了一个字节。缺点(正如一些 cmets 指出的那样)是一些客户端可能难以区分 NULL 和空字符串。甚至一些关系数据库(例如 Oracle)也不区分零长度字符串和 NULL。 这很聪明!我曾经编写聪明的代码,现在我像避免瘟疫一样避免它。我现在希望我的代码有清晰的意图,而不仅仅是正确的行为。我的建议?仅当您想混淆必须支持代码/数据库的任何人时才这样做。例如,在 PHP 中,''
和 null
都是假值。
@CJDennis 如果您已将数据库层抽象为存储库模式,则不必担心此解决方案的晦涩难懂。【参考方案3】:
对于 MySQL 5.0.3 及更高版本,您可以使用BIT
。手册说:
从 MySQL 5.0.3 开始,BIT 数据类型用于存储位域 价值观。一种 BIT(M) 可以存储 M 位值。 M可以范围 从 1 到 64。
否则,根据 MySQL 手册,您可以使用 BOOL
或 BOOLEAN
,它们目前是 tinyint(1) 的别名:
Bool、Boolean:这些类型是TINYINT(1) 的同义词。一个值 零被认为是错误的。非零 值被认为是真实的。
MySQL 还声明:
我们打算实现完整的布尔值 类型处理,按照 标准 SQL,未来的 MySQL 释放。
参考:http://dev.mysql.com/doc/refman/5.5/en/numeric-type-overview.html
【讨论】:
是的,我会选择这个或 CHAR(1) 并根据上下文存储 'Y'/'N' 或 'T'/'F' 等。使用小整数类型的优点是您可以获得跨 RDBMS-es 的最大可移植性 使用 char,至少在 PHP 中,将导致更多代码,因为!$boolean
将永远不会正确评估而无需进一步处理。
@Pecerier 没有什么你不能自己用谷歌搜索的,但是好吧,我会咬一口的。首先,请看一下data0type.h。请注意,innodb 并没有在本地定义 BIT 类型。如果它会以您描述的方式处理 BIT 字段,那么我们肯定会在那里找到它存在的一些暗示。其次,阅读mysqlperformanceblog.com/2008/04/23/…。不要犹豫,告诉我们“市场”中哪些出色的 MySQL 客户端与 BIT 字段配合得很好。对于错过那篇文章的人来说,它们无疑会派上用场。
当我从标准 mysql 命令行客户端位字段中进行选择时,显示完全空白。因此,我更喜欢 TINYINT(1)。
@MikePurcell 我不想问,但您为什么要在表示布尔值的列上使用 auto_increment
?【参考方案4】:
您可以使用 BOOL、BOOLEAN 数据类型来存储布尔值。
这些类型是 TINYINT(1) 的同义词
但是,BIT(1) 数据类型更适合存储布尔值(true[1] 或 false[0]),但 TINYINT(1) 在输出数据时更易于使用,查询等,实现MySQL与其他数据库的互操作。也可以查看this answer or thread。
MySQL 还将 BOOL、BOOLEAN 数据类型转换为 TINYINT(1)。
进一步,阅读documentation
【讨论】:
【参考方案5】:由于 MySQL (8.0.16) 和 MariaDB (10.2.1) 都实现了 CHECK 约束,我现在将使用
bool_val TINYINT CHECK(bool_val IN(0,1))
您将只能存储0
、1
或NULL
,以及可以转换为0
或1
且不会出现'1'
、0x00
、@ 等错误的值987654331@或TRUE
/FALSE
.
如果您不想允许 NULL,请添加 NOT NULL
选项
bool_val TINYINT NOT NULL CHECK(bool_val IN(0,1))
请注意,使用TINYINT
、TINYINT(1)
或TINYINT(123)
几乎没有区别。
如果您希望您的架构向上兼容,您还可以使用BOOL
或BOOLEAN
bool_val BOOL CHECK(bool_val IN(TRUE,FALSE))
db<>fiddle demo
【讨论】:
枚举(0, 1)呢 @santiagoariztiENUM
(必须是 enum('0', '1')
- 注意:这些是字符串)不是一个好主意。 too many issues 是由于它在内部的存储方式以及非字符串值的处理方式。例如。 0
和 FALSE
无法存储。 1
和 TRUE
变为 '0'
。而2
变成了'1'
。
最佳答案...对于那些使用 MySQL 8+ 的人
这应该得到更多的支持!【参考方案6】:
在这里阅读答案后,我决定使用bit(1)
,是的,它在空间/时间上更好,但是一段时间后我改变了主意,我再也不会使用它了。当使用准备好的语句、库等 (php) 时,这让我的开发变得非常复杂。
从那以后,我一直用tinyint(1)
,好像还不错。
【讨论】:
请解释一下它以何种方式使您的开发变得复杂? @ChazyChaz 它期望 true/false 而不是 1/0,这与 SQL Server 等其他一些数据库不同。这有时会导致奇怪的情况,您认为您将其设置为 true,但实际上并没有发生。【参考方案7】:在 MySQL 实现 bit 数据类型之前,如果您的处理确实需要空间和/或时间,例如处理大量事务,请为所有布尔变量创建一个名为 bit_flags
的 TINYINT 字段,并对布尔变量进行屏蔽和移位您在 SQL 查询中需要的位。
例如,如果您最左边的位代表您的 bool 字段,而最右边的 7 个位不代表任何内容,那么您的 bit_flags
字段将等于 128(二进制 10000000)。屏蔽(隐藏)最右边的七个位(使用按位运算符&
),并将第 8 位向右移动七个空格,以 00000001 结尾。现在整个数字(在本例中为 1)是您的价值。
SELECT (t.bit_flags & 128) >> 7 AS myBool FROM myTable t;
if bit_flags = 128 ==> 1 (true)
if bit_flags = 0 ==> 0 (false)
您可以在测试时运行这样的语句
SELECT (128 & 128) >> 7;
SELECT (0 & 128) >> 7;
等等
由于您有 8 位,因此您可能有来自一个字节的 8 个布尔变量。一些未来的程序员总是会使用接下来的七位,所以你必须屏蔽。不要只是转变,否则您将来会为自己和他人制造地狱。确保你有 MySQL 做你的掩码和转换——这将比使用 web 脚本语言(PHP、ASP 等)做的快得多。另外,请确保在 MySQL 注释字段中为您的 bit_flags
字段添加注释。
在实施此方法时,您会发现这些网站很有用:
MySQL — Bit Functions and Operators Decimal/Binary Conversion Tool【讨论】:
这似乎是一种可怕的方式来混淆未来程序员的意图。当然,保存 7 个字节似乎很麻烦(假设您在单个表中使用了所有 8 个布尔值!) @yep 根本没有混淆!编写 documentation 和 MySQL cmets 解释表中的每个字段(如答案所述)!建议的 MySQL 取消屏蔽策略看起来很可靠,并且 存储多达 16 个不同的布尔字段,仅使用 几列 比使用 16 个列要好。如果使用位操作过于混乱,并且您更喜欢使用 Web 脚本语言来获取每个布尔值,只需将其存储为VARCHAR
并在代码中执行取消屏蔽过程(您也不需要将其限制为8 个字段)...
BIT
类型存在。见dev.mysql.com/doc/refman/8.0/en/bit-type.html【参考方案8】:
我厌倦了在 PHP、MySql 和 POST 值的循环中准确地获取零、NULLS 和 '',所以我只使用了 'Yes' 和 'No'。
这工作完美无缺,不需要不明显且容易做到的特殊处理。
【讨论】:
如果您真的想浪费这么多空间并牺牲性能,您至少可以使用 Y 和 N 选项完成 CHAR(1)。 在大多数现实世界的情况下,“不”和仅仅没有信息之间存在真正的区别。例如,如果用户实际上还没有说“不”,您可能希望默认选中一个复选框。你认为你到底节省了多少空间,每次你需要区分假和空时你做了多少处理——如果你真的能区分的话?在存储图像和数字视频的世界中,节省一两个空间完全无关紧要,但清晰度和减少处理是真实的。 这个答案没有错,因为它会起作用,而且没有人们认为的那么糟糕。对于大多数项目(即:表大小 +1 来自我使用 ENUM 数据类型。我个人更喜欢这种表示法:ENUM('y','n')。作为所有布尔标志的应用程序级约定,它紧凑(只有一个字节长)、直观且美观。您可以直接将它与 HTML 表单字段一起使用。例如使用 PHP: 大声笑,这让我大吃一惊,但我不得不说@GeoffKendall 是对的。在很多情况下,不需要最佳性能,任何适合您的方法都是正确的方法。【参考方案9】:这是一个我非常欣赏的优雅解决方案,因为它使用零数据字节:
some_flag CHAR(0) DEFAULT NULL
要将其设置为 true,请设置 some_flag = ''
;要将其设置为 false,请设置 some_flag = NULL
。
然后测试是否为真,检查是否为some_flag IS NOT NULL
,如果为假,检查是否为some_flag IS NULL
。
(此方法在 Jon Warren Lentz、Baron Schwartz 和 Arjen Lentz 的“高性能 MySQL:优化、备份、复制等”中进行了描述。)
【讨论】:
花哨的把戏!如果使用 MySQL 可能“快”,但它会混淆数据,因此任何新开发人员都不知道该列代表什么。 这是使用与 BIT(1) 相同的字节数 祝你好运,让 ORM 很好地映射到这个。 我同意@Richthofen 的观点,并且很难想象在哪种情况下我会提倡使用此解决方案。但是,如果要使用它,那么在列的定义中将NULL
表示为假,''
表示为真,将其指定为COMMENT
,这可能有助于未来的理解。【参考方案10】:
参考此链接 Boolean datatype in Mysql,根据应用使用情况,如果只想存储0或1,bit(1)是更好的选择。
【讨论】:
确实BIT(1)
只允许存储b'0'
或b'1'
值。 BIT
数据类型的最大问题是各种客户端库对数据类型的处理方式各不相同。检查各种 SQL 工具(SQLyog、TOAD for MySQL、SQL Developer)、“逆向工程”数据库模型的工具和各种客户端(如 JDBC、PHP、Perl DBI)中的行为,并测试一些 ORM 框架(休眠,Mybatis,JPA)。在易用性、工具/框架兼容性/原生支持方面,TINYINT(1)
无疑是赢家。
是的。它的完成取决于应用程序正在考虑的框架。例如,PHP 的 Phalcon 框架不处理 Bit 数据类型
郑重声明,MyBatis 同时支持BIT
和TINYINT
。参考 MyBatis 的 JdbcType 类,mybatis.org/mybatis-3/apidocs/reference/org/apache/ibatis/type/…
@Vidz 提到 BIT(1),我给你加一个,但也会向阅读本文的开发人员指出 - 了解将在应用层/数据访问层中使用的语言并知道你的图书馆支持。我目前正在使用 Java,此时对于像 Hybernate 和使用 JDBC 这样的库,建议选择 BIT(1)。这是 URL [参见表 5.2]:dev.mysql.com/doc/connector-j/en/…【参考方案11】:
如果您有很多布尔字段,Bit 仅优于各种字节选项(tinyint、enum、char(1))。一个位字段仍然占用一个完整字节。两个位字段适合同一个字节。三、四、五、六、七、八。之后他们开始填充下一个字节。最终节省的成本是如此之小,您应该关注数以千计的其他优化。除非您要处理大量数据,否则这几个字节不会加起来太多。如果您在 PHP 中使用 bit,则需要对输入和输出的值进行类型转换。
【讨论】:
+1 用于类型转换注释。为了在使用编程语言时添加这一点,请避免使用惰性编程技术以保持一致性。使用相同的运算符,而不仅仅是等于。对于 PHP,if( $var == "" ) 对于 0、false、null、undefined 和 "" 将是 true。要测试所有值,通常最好使用 if( true === empty( $var ) ),因为它也可以避免未定义的错误。您还应该验证您正在使用的数据类型 if( is_int( $var ) && $var === 0 ) 或将其强制转换为任务的特定数据类型 (int) $var。 @Thor 对于 MySQL 是否如此,对于 MSSQL 也是如此?我正在将尚未投入生产的新应用程序从 MSSQL 迁移到 MySQL。我没有使用 PHP,而是将 C# 转换为 Java 8。鉴于 Java 是一种强类型语言,我并不担心类型处理......只是所有的位标志都会从一个字节移动到最多 8 个标志到给定 TINYINT(1) 的每个标志 1 个字节。你知道关于这个主题的任何 MySQL 文档吗? @Thor 做一些更深入的研究很清楚答案应该是什么。确实发生了变化,我们已经看到了这种处理方式的改进。了解您将在应用层/数据访问层中使用的语言,并了解您的库支持。我目前正在使用 Java,此时对于像 Hybernate 和使用 JDBC 这样的库,建议选择 BIT(1)。这是 URL [参见表 5.2]:dev.mysql.com/doc/connector-j/en/… 用资源链接证明 8 bit(1) 列占用相同的字节...我已经搜索了一段时间,但在任何地方都找不到...【参考方案12】:如果您使用 BOOLEAN 类型,则别名为 TINYINT(1)。如果您想使用标准化 SQL 并且不介意该字段可能包含超出范围的值(基本上任何不为 0 的值都将是“真”),这是最好的选择。
ENUM('False', 'True') 将允许您在 SQL 中使用字符串,并且 MySQL 将在内部将字段存储为整数,其中 'False'=0 和 'True'=1 根据顺序枚举已指定。
在 MySQL 5+ 中,您可以使用 BIT(1) 字段来指示 1 位数字类型。我不相信这实际上会使用更少的存储空间,但再次允许您将可能的值限制为 1 或 0。
以上所有内容将使用大致相同数量的存储空间,因此最好选择您认为最容易使用的存储空间。
【讨论】:
您关于 ENUM 的评论不正确:尝试 CAST(yourenumcol AS UNSIGNED),您会注意到 False 为 1,True 为 2。ENUM 的另一个问题是它也是易于插入''(空字符串)。我不推荐使用这个。 根据我的经验,使用 PHP 代码中的 BIT(1) 字段有点麻烦。 TINYINT(1) 更简单,生成的代码更易读。 @M-Peror - “使用 PHP 代码中的 BIT(1) 字段是一个 bit 麻烦”...没有双关语的意思。 :) 但是,是的,我同意。我记得 TINYINT(1) 也更容易......只是不记得为什么。其他人对此有想法吗? BIT(1) 表面上看起来更好,因为您可以限制为 0 或 1。我认为 BIT 有时被解释为二进制数据(取决于编程语言和驱动程序/库);而 TINYINT 更像是一个数字。 @BMiner - 哈哈,这真的是无意的,没有注意到 :) 但事实上,如果我没记错的话,位字段被解释为二进制,而 tinyint 更容易被视为数字,因此更易于在(布尔)表达式中使用。【参考方案13】:我使用 TINYINT(1) 来在 Mysql 中存储布尔值。
我不知道使用这个有什么好处...但是如果我没记错的话,mysql可以存储布尔值(BOOL)并将其存储为tinyint(1)
http://dev.mysql.com/doc/refman/5.0/en/other-vendor-data-types.html
【讨论】:
以上是关于用于存储布尔值的 MySQL 数据类型的主要内容,如果未能解决你的问题,请参考以下文章