在这种特定情况下,在 MySql 结构中使用 NULL 的优缺点是什么?
What are the pros and cons of Using NULL in MySql Structure in this specific case?
我有一个 table 结构,如下所示,包含角色结构 Table 我采用:
让它成为一个 "roles" table 包含一些与用户角色相关的记录。
现在,我在这里选择了一个专栏 "is_archived(int)",我用它来了解该角色仍然存在或已删除。
所以我正在考虑该列的两个值:
- "NULL"=> 如果该角色仍然存在(如 TRUE),
- "1" => 如果已删除/不活动(如 FALSE)
我的 table 最大记录将包含此列的 "NULL" 值,默认值也是 "NULL"。
现在我进退两难,因为我使用的是 "NULL" 而不是“0”,所以在这种情况下是否存在任何性能问题。
我需要知道这个案例的优缺点(比如"Search Performance"、"Storage"、"indexing"等)。
如果有缺点,最好的选择是什么?
我的意见是 NULL
是为了 "out of band",而不是为了拼凑一个 in-band 值。如果有任何性能或space差异,那是微不足道的。
对于 true/false,使用 TINYINT NOT NULL
。它只有 1 个字节。您可以使用ENUM('false', 'true')
;它也是 1 个字节。
INT
,无论后面是多少,都占用4个字节。不要将 INT
用于如此低的基数。
让 NULL
表示 "not yet known" 或您还不能说 "true" 或 "false" 的任何其他情况。 (因为你可能总是知道它是否是 'archived',NULL
在这里没有位置。
您甚至可以使用ENUM('male', 'female', 'decline_to_state', 'transgender', 'gay', 'lesbian', 'identifies_as_male', 'North_Carolina_resident', 'other')
。 (注意:这只是部分列表;最好为其设置 table 和 JOIN
。)
我同意@RickJames 关于 NULL 的观点。不要在你想使用像 true
这样的真实值的地方使用 NULL。同样,不要使用像 0 或 ''
这样的真实值来表示没有值。
至于性能影响,您应该知道要搜索 NULL 的 presence/absence,您将使用谓词 is_archive IS [NOT] NULL
。
如果您在查询中使用 EXPLAIN,您会看到该谓词算作 "range" 访问类型。而搜索单个特定值,例如is_archive = 1
或 is_archive = 0
是 "ref" 访问类型。
这将对某些查询产生性能影响。例如,如果您在 (is_archived, created_on) 上有一个索引,并且您尝试执行如下查询:
SELECT ... FROM roles
WHERE is_archived IS NULL AND created_on = '2017-01-31'
那么索引只会是half-useful。 WHERE 子句无法搜索索引中的第二列。
但如果您使用实数值,则查询如下:
SELECT ... FROM roles
WHERE is_archived = 0 AND created_on = '2017-01-31'
将使用索引中的两列。
关于 NULL 存储的评论:
是的,在InnoDB存储引擎中,内部每行存储一个位域,每列1位,位表示每列是否为NULL。这些位被紧凑地存储,即一个字节最多包含 8 位。位域之后是一系列列值。 NULL 列不存储任何值。所以是的,从技术上讲,使用 NULL 确实可以减少存储空间。
但是,我建议您简化数据管理并在您想要 false
时使用 false
。不要将 NULL 用作您的值之一。我想如果您以每行保存一个字节很重要的规模管理数据,那么会有一个例外。例如,如果您管理着数百亿行。
但在比这更小的规模下,潜在的 space 节省并不值得您为项目增加额外的复杂性。
换句话说,InnoDB 页面只填满了每个数据页的 15/16。因此,InnoDB 页面格式的开销可能大于您从 micro-optimizing 布尔存储中获得的节省。
我有一个 table 结构,如下所示,包含角色结构 Table 我采用:
让它成为一个 "roles" table 包含一些与用户角色相关的记录。 现在,我在这里选择了一个专栏 "is_archived(int)",我用它来了解该角色仍然存在或已删除。
所以我正在考虑该列的两个值:
- "NULL"=> 如果该角色仍然存在(如 TRUE),
- "1" => 如果已删除/不活动(如 FALSE)
我的 table 最大记录将包含此列的 "NULL" 值,默认值也是 "NULL"。
现在我进退两难,因为我使用的是 "NULL" 而不是“0”,所以在这种情况下是否存在任何性能问题。
我需要知道这个案例的优缺点(比如"Search Performance"、"Storage"、"indexing"等)。
如果有缺点,最好的选择是什么?
我的意见是 NULL
是为了 "out of band",而不是为了拼凑一个 in-band 值。如果有任何性能或space差异,那是微不足道的。
对于 true/false,使用 TINYINT NOT NULL
。它只有 1 个字节。您可以使用ENUM('false', 'true')
;它也是 1 个字节。
INT
,无论后面是多少,都占用4个字节。不要将 INT
用于如此低的基数。
让 NULL
表示 "not yet known" 或您还不能说 "true" 或 "false" 的任何其他情况。 (因为你可能总是知道它是否是 'archived',NULL
在这里没有位置。
您甚至可以使用ENUM('male', 'female', 'decline_to_state', 'transgender', 'gay', 'lesbian', 'identifies_as_male', 'North_Carolina_resident', 'other')
。 (注意:这只是部分列表;最好为其设置 table 和 JOIN
。)
我同意@RickJames 关于 NULL 的观点。不要在你想使用像 true
这样的真实值的地方使用 NULL。同样,不要使用像 0 或 ''
这样的真实值来表示没有值。
至于性能影响,您应该知道要搜索 NULL 的 presence/absence,您将使用谓词 is_archive IS [NOT] NULL
。
如果您在查询中使用 EXPLAIN,您会看到该谓词算作 "range" 访问类型。而搜索单个特定值,例如is_archive = 1
或 is_archive = 0
是 "ref" 访问类型。
这将对某些查询产生性能影响。例如,如果您在 (is_archived, created_on) 上有一个索引,并且您尝试执行如下查询:
SELECT ... FROM roles
WHERE is_archived IS NULL AND created_on = '2017-01-31'
那么索引只会是half-useful。 WHERE 子句无法搜索索引中的第二列。
但如果您使用实数值,则查询如下:
SELECT ... FROM roles
WHERE is_archived = 0 AND created_on = '2017-01-31'
将使用索引中的两列。
关于 NULL 存储的评论:
是的,在InnoDB存储引擎中,内部每行存储一个位域,每列1位,位表示每列是否为NULL。这些位被紧凑地存储,即一个字节最多包含 8 位。位域之后是一系列列值。 NULL 列不存储任何值。所以是的,从技术上讲,使用 NULL 确实可以减少存储空间。
但是,我建议您简化数据管理并在您想要 false
时使用 false
。不要将 NULL 用作您的值之一。我想如果您以每行保存一个字节很重要的规模管理数据,那么会有一个例外。例如,如果您管理着数百亿行。
但在比这更小的规模下,潜在的 space 节省并不值得您为项目增加额外的复杂性。
换句话说,InnoDB 页面只填满了每个数据页的 15/16。因此,InnoDB 页面格式的开销可能大于您从 micro-optimizing 布尔存储中获得的节省。