【问题标题】:Why can't a text column have a default value in MySQL?为什么文本列在 MySQL 中不能有默认值?
【发布时间】:2011-03-28 20:50:55
【问题描述】:

如果你尝试在一个表上创建一个 TEXT 列,并在 MySQL 中给它一个默认值,你会得到一个错误(至少在 Windows 上)。我看不出文本列不应该有默认值的任何原因。 MySQL 文档没有给出解释。这对我来说似乎不合逻辑(而且有点令人沮丧,因为我想要一个默认值!)。有人知道为什么不允许这样做吗?

【问题讨论】:

  • 我们可以看到您使用的查询吗?
  • 您确定要一个 TEXT 列,而不是一个 VARCHAR 列吗? TEXT 列用于长度可能超过 255 个字节的内容。
  • 这应该是一条评论。另外,是的,他的意思是TEXT - 这些列不能有默认值。 VARCHAR可以。
  • 如果您使用 phpmyadmin 设置数据库,可能需要调查 mysql gui 工具/工作台... ;)
  • 是的,很遗憾,我需要超过 255 个字符。

标签: mysql default-value


【解决方案1】:

Windows MySQL v5 会引发错误,但 Linux 和其他版本只会引发警告。这需要修复。 WTF?

另请参阅 MySQL Bugtracker 中将其修复为错误 #19498 的尝试:

布莱斯·内斯比特 2008 年 4 月 4 日下午 4:36:
在 MS Windows 上,“no DEFAULT”规则是一个错误,而在其他平台上它通常是一个警告。虽然不是错误,但如果您在宽松的平台上编写代码,然后在严格的平台上运行它,则可能会被此困住:

就个人而言,我确实认为这是一个错误。在 Google 上搜索“BLOB/TEXT 列不能有默认值”会返回大约 2,940 个结果。其中大多数是在尝试安装在一个系统上运行但在其他系统上运行的数据库脚本时出现不兼容的报告。

我现在在为我的一个客户修改的 web 应用程序上遇到了同样的问题,最初部署在 Linux MySQL v5.0.83-log 上。我正在运行 Windows MySQL v5.1.41。即使尝试使用最新版本的 phpMyAdmin 来提取数据库,它也不会报告相关文本列的默认值。然而,当我尝试在 Windows 上运行插入(在 Linux 部署上运行良好)时,我收到 ABC 列上没有默认值的错误。我尝试使用明显的默认值(基于该列的唯一值的选择)在本地重新创建表,并最终收到非常有用的 BLOB/TEXT 列不能有默认值.

同样,不保持跨平台的基本兼容性是不可接受的,并且是一个错误。


如何在 MySQL 5 (Windows) 中禁用严格模式:

  • 编辑 /my.ini 并查找行

    sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
    
  • 替换为

    sql_mode='MYSQL40'
    
  • 重启MySQL服务(假设是mysql5)

    net stop mysql5
    net start mysql5
    

如果你有 root/admin 访问权限,你也许可以执行

mysql_query("SET @@global.sql_mode='MYSQL40'");

【讨论】:

  • 如果您有 root 访问权限并且正在使用 phpMyAdmin,请转到主页(单击 phpMyAdmin 徽标),转到“变量”选项卡,找到 sql_mode 变量,然后单击“编辑”。
  • 我使用的是 CentOS 5.8 和 MySQL v 14.14 Distrib 5.1.71 在尝试为 TEXT 字段设置默认值时抛出错误而不是警告。只是想注意它并非适用于所有 Linux 平台。
  • OS X 这些天至少似乎抛出了一个错误。文档dev.mysql.com/doc/refman/5.7/en/blob.html 说“BLOB 和 TEXT 列不能有 DEFAULT 值。” FWIW(但不是为什么)
  • 我也认为这是一个错误。这让我很沮丧,因为缺少默认值会迫使您将每个 TEXT 列设置为允许 NULL,或者它会强制您在插入时指定一个值,因为您甚至无法将空字符串指定为默认值。关闭STRICT_TRANS_TABLES 对我来说不是一个好的解决方法,因为我想要打开它有很多原因。另外,我遇​​到了兼容性问题。例如,Postgres 允许在文本列上设置默认值;这个因素让我只想迁移到,而不是从 postgres。
【解决方案2】:

如果没有深入了解 mySQL 引擎,我会说这听起来像是一种节省内存的策略。我认为原因是 docs 的这一段背后的原因:

每个 BLOB 或 TEXT 值在内部由一个单独分配的对象表示。这与所有其他数据类型形成对比,所有其他数据类型在打开表时每列分配一次存储空间。

似乎预填充这些列类型会导致内存使用和性能损失。

【讨论】:

  • -1:在 TEXT 列中存储数据(例如城市名称)实际上比在 CHAR 或 VARCHAR 列中存储相同数据占用的总内存更少。
  • @david 我引用的手册章节不是关于存储,而是检索。
  • 我看不出这会如何导致任何异常的内存使用和性能损失。显然,当用户定义默认值时,无论数据类型是什么(尤其是批量操作),他都希望性能受到影响。但是,据我了解,您说对于 BLOB/TEXT 字段,与其他数据类型相比,此性能影响相对较高?这与 BLOB/TEXT 作为一个单独的对象在内部存储这一事实有何关系?这对我来说没有任何意义。
  • 恕我直言,这不是一种节省内存的策略。它要么是一个错误,要么是写它的人疯了。我认为是后者,因为他们现在至少 8 年都无法修复它。每个其他数据库都具有的基本功能。
  • 没关系,对于想要使用它的人来说,它仍然应该是一个选择。
【解决方案3】:

作为主要问题:

有人知道为什么不允许这样做吗?

仍然没有回答,我快速搜索并在MySQL Bugs 上找到了一位 MySQL 开发人员提供的相对较新的内容:

[2017 年 3 月 17 日 15:11] Ståle Deraas

开发者发布:

这确实是一个有效的功能请求,乍一看,添加它似乎微不足道。但是 TEXT/BLOBS 值不直接存储在用于读取/更新表的记录缓冲区中。所以为它们分配默认值有点复杂。

这不是一个明确的答案,但至少是为什么问题的起点。

同时,我将围绕它编写代码,或者使列可以为空,或者从应用程序代码中为每个insert 显式分配一个(默认'')值...

【讨论】:

  • 没有其他类型可以用来代替 TEXT 吗?如果 255 个字符足够,请使用 VARCHAR(255)。但是TINYTEXT 呢?似乎 VARCHAR(4096) 也可以使用
【解决方案4】:

“在 TEXT/BLOB 列中支持 DEFAULT” 是一个 feature request in the MySQL Bugtracker (Bug #21532).

我知道我不是唯一一个想在 TEXT 列中设置默认值的人。 我认为这个特性应该会在以后的 MySQL 版本中得到支持。

这在 MySQL 5.0 版本中无法修复, 因为如果有人试图在不支持该功能的(当前)数据库和支持该功能的任何数据库之间来回传输数据库,显然会导致不兼容和数据丢失。

【讨论】:

  • 在我看来,对于允许为空的 TEXT 列,您应该能够在 "" 和 NULL 之间更改它。好像做不到。
【解决方案5】:

使用触发器可以获得与默认值相同的效果

create table my_text

(
   abc text
);

delimiter //
create trigger mytext_trigger before insert on my_text
for each row
begin
   if (NEW.abc is null ) then
      set NEW.abc = 'default text';
   end if;
end
//
delimiter ;

【讨论】:

    【解决方案6】:

    我通常在 Linux 上运行网站,但我也在本地 Windows 机器上开发。我已经多次遇到这个问题,并且在遇到问题时只是修复了表格。我昨天安装了一个应用程序来帮助某人,当然又遇到了问题。所以,我决定是时候弄清楚发生了什么——并找到了这个线程。我真的不喜欢将服务器的 sql_mode 更改为早期模式(默认情况下)的想法,所以我想出了一个简单(我认为)的解决方案。

    此解决方案当然需要开发人员封装他们的表创建脚本,以弥补在 Windows 上运行的 MySQL 问题。您将在转储文件中看到类似的概念。一个重要的警告是,如果使用分区,这可能/将会导致问题。

    // Store the current sql_mode
    mysql_query("set @orig_mode = @@global.sql_mode");
    
    // Set sql_mode to one that won't trigger errors...
    mysql_query('set @@global.sql_mode = "MYSQL40"');
    
    /**
     * Do table creations here...
     */
    
    // Change it back to original sql_mode
    mysql_query('set @@global.sql_mode = @orig_mode');
    

    就是这样。

    【讨论】:

    • 这并没有解决为什么 MySQL 有这种行为的问题,但感谢您分享您的方法,以便其他人也可以受益。欢迎来到 Stack Overflow!
    • 是的,我知道...我必须进一步研究 STRICT 模式,看看它是否有意义——因为 MySQL 在 Nix 的盒子上抛出警告,但在 Windows 盒子上失败。这表明无论平台如何,实施都可能出现问题。您会注意到在 MySQL 的文档中,有这样的通知:“BLOB 和 TEXT 列不能有 DEFAULT 值。”因此,从逻辑上讲,似乎 5 之前版本中的实现实际上在所有平台上都被破坏了。
    【解决方案7】:

    对于 Ubuntu 16.04:

    如何在 MySQL 5.7 中禁用严格模式:

    编辑文件/etc/mysql/mysql.conf.d/mysqld.cnf

    如果mysql.cnf中存在下一行

    sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
    

    然后替换为

    sql_mode='MYSQL40'
    

    否则

    只需在 mysqld.cnf 中添加以下行

    sql_mode='MYSQL40'
    

    这解决了问题。

    【讨论】:

    • 这很好,如果你想禁用严格模式。但是,如果您想保持启用它,我认为可能没有解决方案,这很不幸,因为我在这条船上。我认为唯一的选择是允许 NULL 值(我不想要)并将其作为默认值。
    【解决方案8】:

    MySQL 8.0.13, released 2018-10-22 添加了对使用表达式作为默认值的支持,适用于TEXTJSONBLOBGEOMETRY

    你还是不能写:

    create table foo(bar text default 'baz')
    

    但你现在可以写:

    create table foo(bar text default ('baz'))
    

    实现相同的目标。

    【讨论】:

      【解决方案9】:

      这是一个非常古老的问题,但似乎仍未得到正确回答。而且,我的这个答案并不是对这个问题的实际答案——“为什么文本列不能有默认值”,但是因为不可能在评论中写长文本,而且我的评论可以帮助某人防止错误,这是一个单独的答案:

      有些人说错误是由于操作系统 - Windows-Linux 而发生的;但这与操作系统没有直接关系。 (不过,不同操作系统的不同安装程序中 MySQL 的默认设置可能存在差异,我不确定。)

      主要原因是标志STRICT_TRANS_TABLES 用于sql_mode 设置。如果在 INSERT 语句中没有为 TEXT 数据类型列指定值并且如果标志存在于 sql_mode 设置中,则 MySQL 报告错误;如果该标志不存在,则 MySQL 仅报告警告并插入记录。

      所以,为了防止这个错误,可以从 MySQL 的 sql_mode 设置中删除 STRICT_TRANS_TABLES。 (如果会影响数据库上的其他操作,我需要将模式重置为以前的值。)

      根据 MySQL 中的documentation of SQL mode ...

      对于STRICT_TRANS_TABLES,MySQL 将无效值转换为最接近该列的有效值并插入调整后的值。如果缺少值,MySQL 会插入列数据类型的隐式默认值。在任何一种情况下,MySQL 都会生成警告而不是错误并继续处理该语句。隐式默认值在第 11.6 节“数据类型默认值”中进行了描述。

      ...和documentation of Data Type Default Values ...

      BLOBTEXTGEOMETRYJSON 数据类型不能指定默认值。

      ...TEXT 列不能有默认值,但如果从sql_mode 中删除STRICT_TRANS_TABLES,则如果没有为INSERT 中的TEXT 列指定值,MySQL 将插入空字符串''声明。

      【讨论】:

        猜你喜欢
        • 2014-07-18
        • 2015-03-16
        • 2011-09-17
        • 2017-01-15
        • 2020-03-05
        • 1970-01-01
        • 2019-04-21
        相关资源
        最近更新 更多