ChatGPT解决这个技术问题 Extra ChatGPT

用于存储布尔值的 MySQL 数据类型

由于 MySQL 似乎没有任何“布尔”数据类型,您“滥用”哪种数据类型在 MySQL 中存储真/假信息?

特别是在编写和读取 PHP 脚本的上下文中。

随着时间的推移,我使用并看到了几种方法:

tinyint,包含值 0/1 的 varchar 字段,

包含字符串 '0'/'1' 或 'true'/'false' 的 varchar 字段

最后枚举包含“真”/“假”两个选项的字段。

以上似乎都不是最佳的。我倾向于使用 tinyint 0/1 变体,因为 PHP 中的自动类型转换为我提供了相当简单的布尔值。

那么您使用哪种数据类型?有没有为我忽略的布尔值设计的类型?您是否看到使用一种或另一种类型的任何优点/缺点?

任何正在阅读此问题旧答案的人都需要了解 MySQL 在版本 5 中添加了位数据类型。尽可能使用该信息。 dev.mysql.com/doc/refman/5.0/en/bit-type.html
对于当前版本的 MYSQL 布尔类型可用 - dev.mysql.com/doc/refman/5.5/en/numeric-type-overview.html 选中此项。根据那个值零被认为是假的
bit(1) 在 Excel 中导入有点**。切换到 tinyint(1) 有效。
现在我们有 5 年后的布尔值

f
flow2k

对于 MySQL 5.0.3 及更高版本,您可以使用 BIT。手册说:

从 MySQL 5.0.3 开始,BIT 数据类型用于存储位域值。一种 BIT(M) 可以存储 M 位值。 M 的范围可以从 1 到 64。

否则,根据 MySQL 手册,您可以使用 BOOLBOOLEAN,它们目前是 tinyint(1) 的别名:

Bool、Boolean:这些类型是 TINYINT(1) 的同义词。零值被认为是错误的。非零值被认为是真的。

MySQL 还指出:

我们打算在未来的 MySQL 版本中根据标准 SQL 实现完整的布尔类型处理。

参考文献:http://dev.mysql.com/doc/refman/5.5/en/numeric-type-overview.html


是的,我会选择这个或 CHAR(1) 并根据上下文存储 'Y'/'N' 或 'T'/'F' 等。使用小整数类型的优点是您可以获得跨 RDBMS-es 的最大可移植性
至少在 PHP 中使用 char 将导致更多代码,因为如果没有进一步处理,!$boolean 将永远无法正确评估。
解决方案是将 Laravel 模型中的列 $casts 为布尔值,如 protected $casts = [ 'shipped' => 'boolean', 'refund' => 'boolean', ];
i
informatik01

BOOLBOOLEANTINYINT(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。
M
Martijn Pieters

这是一个我非常欣赏的优雅解决方案,因为它使用零数据字节:

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 <5 甚至可能比 BIT 更轻的占用空间,这很有帮助,但是为了遵守约定和稍微减少计算开销(逻辑与精确值),我会说 BIT 是更好的方法。
可能“快”,但它混淆了数据,因此任何新开发人员都不知道该列代表什么。
这使用与 BIT(1) 相同的字节数
祝你好运,让 ORM 很好地映射到这一点。
我同意@Richthofen 的观点,并且很难想象我会提倡使用这种解决方案的情况。但是,如果要使用它,那么在列的定义中将 NULL 表示为 false 而 '' 表示为 true 指定为 COMMENT 可能对帮助未来理解有一些非常小的帮助。
i
informatik01

这个问题已经得到解答,但我想我会投入 0.02 美元。我经常使用 CHAR(0),其中 '' == true and NULL == false

MySQL docs

当您需要一个只能取两个值的列时,CHAR(0) 也非常好:定义为 CHAR(0) NULL 的列只占用一位并且只能取值 NULL 和 ''(空字符串) .


mm,你我这好像是自找麻烦。我的意思是,根据语言的不同,很容易发现 NULL 和 '' 之间的差异(例如 PHP)。
在节省空间(用于表示布尔值的字节数)方面,这种方法显然是赢家。这在 TINYINT 上节省了一个字节。缺点(正如一些评论指出的那样)是一些客户端可能难以区分 NULL 和空字符串。甚至一些关系数据库(例如 Oracle)也不区分零长度字符串和 NULL。
这是非常聪明的!我曾经编写聪明的代码,现在我像避免瘟疫一样避免它。我现在希望我的代码有清晰的意图,而不仅仅是正确的行为。我的建议?仅当您想混淆必须支持代码/数据库的任何人时才这样做。例如,在 PHP 中,''null 都是假值。
@CJDennis 如果您在存储库模式后面抽象了数据库层,则不必担心此解决方案的晦涩难懂。
C
Ciaran McNulty

如果您使用 BOOLEAN 类型,则别名为 TINYINT(1)。如果您想使用标准化 SQL 并且不介意该字段可能包含超出范围的值(基本上任何不为 0 的值都将是“真”),这是最好的选择。

ENUM('False', 'True') 将允许您在 SQL 中使用字符串,MySQL 将在内部将字段存储为整数,其中 'False'=0 和 'True'=1 根据 Enum 指定的顺序.

在 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) 字段有点麻烦”......没有双关语的意思。 :) 但是,是的,我同意。我记得 TINYINT(1) 也更容易......只是不记得为什么。其他人对此有想法吗? BIT(1) 表面上看起来更好,因为您可以限制为 0 或 1。我认为 BIT 有时被解释为二进制数据(取决于编程语言和驱动程序/库);而 TINYINT 更像是一个数字。
@BMiner - 哈哈,这真的是无意的,没有注意到:) 但事实上,如果我没记错的话,位字段被解释为二进制,而 tinyint 更容易被视为数字,因此更容易在(布尔)表达式中使用。
F
Fred

我使用 TINYINT(1) 来在 Mysql 中存储布尔值。

我不知道使用它是否有任何优势...但是如果我没记错的话,mysql可以存储布尔值(BOOL)并将其存储为tinyint(1)

http://dev.mysql.com/doc/refman/5.0/en/other-vendor-data-types.html


T
Thor

如果您有很多布尔字段,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,此时 BIT(1) 是 Hybernate 等库和使用 JDBC 的推荐选择。这是 URL [参见表 5.2]:dev.mysql.com/doc/connector-j/en/…
用资源链接证明 8 位(1)列占用相同的字节......我已经搜索了一段时间,但我在任何地方都没有找到它......
C
Cody Gray

我厌倦了试图在 PHP、MySql 和 POST 值的循环中准确地获取零、NULLS 和“”,所以我只使用“是”和“否”。

这完美无瑕,不需要不明显且容易做到的特殊处理。


如果您真的想浪费这么多空间并牺牲性能,您至少可以使用 Y 和 N 选项完成 CHAR(1)。
在大多数现实世界的情况下,“不”和仅仅没有信息之间存在真正的区别。例如,如果用户实际上还没有说“不”,您可能希望默认选中一个复选框。你认为你到底节省了多少空间,每次你需要区分假和空时你做了多少处理——如果你真的可以区分的话?在存储图像和数字视频的世界中,节省一两个空间完全无关紧要,但清晰度和减少处理是真实的。
这个答案没有错,因为它会起作用,而且没有人们认为的那么糟糕。对于大多数项目(即:表大小 < 1 百万行),所提供解决方案之间的性能差异可以忽略不计。如果我的查询在 7 毫秒和 5 毫秒内返回,我不会抱怨……公平地说,如果您的表增长到 1000 万行或更多,这可能不是首选的解决方案。
我为使用 ENUM 数据类型 +1。我个人更喜欢这种表示法:ENUM('y','n')。作为所有布尔标志的应用程序级约定,它紧凑(仅一个字节长)、直观且美观。您可以直接将它与 HTML 表单字段一起使用。例如使用 PHP:
大声笑,这让我大吃一惊,但我不得不说@GeoffKendall 是对的。在很多情况下,不需要最佳性能,任何适合您的方法都是正确的方法。
T
TRiG

在 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 等)来做这件事要快得多。此外,请确保在您的 bit_flags 字段的 MySQL 评论字段中添加评论。

在实施此方法时,您会发现这些站点很有用:

MySQL — 位函数和运算符

十进制/二进制转换工具


这似乎是一种混淆未来程序员意图的可怕方式。当然,保存 7 个字节似乎很麻烦(假设您在该单个表中使用了所有 8 个布尔值!)
@是的,根本没有混淆!编写 documentation 和 MySQL comments 解释表中的每个字段(如答案所述)!建议的 MySQL 取消屏蔽策略看起来很可靠,并且 存储多达 16 个不同的布尔字段,仅使用 几列 比使用 16 个列要好。如果使用位操作过于混乱,并且您更喜欢使用 Web 脚本语言来获取每个布尔值,只需将其存储为 VARCHAR 并在代码中执行取消屏蔽过程(您也不需要将其限制为8 个字段)...
BIT 类型存在。请参阅dev.mysql.com/doc/refman/8.0/en/bit-type.html
P
Paul Spiegel

由于 MySQL (8.0.16) 和 MariaDB (10.2.1) 都实现了 CHECK 约束,我现在将使用

bool_val TINYINT CHECK(bool_val IN(0,1))

您将只能存储 01NULL,以及可以转换为 01 且不会出现 '1'0x00b'1'TRUE/FALSE

如果您不想允许 NULL,请添加 NOT NULL 选项

bool_val TINYINT NOT NULL CHECK(bool_val IN(0,1))

请注意,使用 TINYINTTINYINT(1)TINYINT(123) 几乎没有区别。

如果您希望架构向上兼容,还可以使用 BOOLBOOLEAN

bool_val BOOL CHECK(bool_val IN(TRUE,FALSE))

db<>fiddle demo


那么枚举(0, 1)呢
@santiagoarizti ENUM(必须是 enum('0', '1') - 注意:这些是字符串)不是一个好主意。由于它在内部的存储方式以及非字符串值的处理方式,因此存在 too many issues。例如。 0FALSE 无法存储。 1TRUE 变为 '0'2 变为 '1'
最佳答案...对于那些使用 MySQL 8+
C
Community

参考此链接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 同时支持 BITTINYINT。参考 MyBatis 的 JdbcType 类,mybatis.org/mybatis-3/apidocs/reference/org/apache/ibatis/type/…
@Vidz 我给你加一个来提到 BIT(1),但也会向阅读这篇文章的开发人员指出 - 了解你的语言将在应用层/数据访问层中,并了解你的库支持。我目前正在使用 Java,此时 BIT(1) 是 Hybernate 等库和使用 JDBC 的推荐选择。这是 URL [参见表 5.2]:dev.mysql.com/doc/connector-j/en/…
L
Lemures

在阅读了这里的答案后,我决定使用 bit(1),是的,它在空间/时间上更好,但是过了一段时间我改变了主意,我再也不会使用它了。当使用准备好的语句、库等(php)时,这使我的开发变得非常复杂。

从那以后,我一直使用tinyint(1),似乎已经足够好了。


愿意解释它以何种方式使您的开发复杂化?
@ChazyChaz 它期望真/假而不是 1/0,这与 SQL Server 等其他一些数据库不同。这有时会导致奇怪的情况,您认为您将其设置为 true 但实际上并没有发生。
P
Premkumar chalmeti

您可以使用 BOOL、BOOLEAN 数据类型来存储布尔值。

这些类型是 TINYINT(1) 的同义词

但是,BIT(1) 数据类型更适合存储布尔值(true[1] 或 false[0]),但 TINYINT(1) 在输出数据、查询等时更易于使用并实现 MySQL 与其他数据库之间的互操作性。您也可以检查 this answer or thread

MySQL 还将 BOOL、BOOLEAN 数据类型转换为 TINYINT(1)。

此外,请阅读 documentation