为什么在MySQL中文本列不能有默认值?

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

194393 次浏览

如果对mySQL引擎没有任何深入的了解,我会说这听起来像是一种节省内存的策略。我假设原因是文档中的这段话:

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

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

Windows MySQL v5会抛出错误,但Linux和其他版本只会抛出警告。这个问题需要解决。WTF ?

也可以在MySQL bug跟踪器中看到修复此bug #19498的尝试:

Bryce Nesbitt on April 4 2008 4:36pm:
在微软Windows上,“no DEFAULT”规则是一个错误,而在其他平台上,它通常是一个警告。虽然这不是一个bug,但如果你在一个宽松的平台上编写代码,然后在一个严格的平台上运行它,就有可能被这个问题困住:

就我个人而言,我认为这是一个bug。在谷歌上搜索“BLOB/TEXT列不能有默认值”会返回大约2,940个结果。其中大多数是在试图安装在一个系统上工作而在其他系统上不工作的DB脚本时的不兼容性报告。

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

同样,不维护跨平台的基本兼容性是不可接受的,这是一个错误。


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

  • 编辑/my.ini并查找line

    sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
    
  • Replace it with

    sql_mode='MYSQL40'
    
  • Restart the MySQL service (assuming that it is mysql5)

    net stop mysql5
    net start mysql5
    

If you have root/admin access you might be able to execute

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

我通常在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');

差不多就是这样。

" TEXT/BLOB列的默认支持" 是一个 MySQL Bug追踪器中的特性请求(Bug #21532) . < / p > 我知道我不是唯一一个想在TEXT列中放置默认值的人。 我认为MySQL的后续版本应该支持这个特性

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

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

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 ;

对于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'

这就解决了问题。

作为主要问题:

有人知道为什么这是不允许的吗?

仍然没有答案,我做了一个快速搜索,并从MySQL开发人员MySQL的bug找到了一个相对较新的补充:

[17 Mar 2017 15:11] Ståle Deraas

开发者发布:

这确实是一个有效的特性请求,乍一看,添加它似乎很简单。但是TEXT/BLOBS值并不是直接存储在用于读取/更新表的记录缓冲区中。因此,为它们分配默认值有点复杂。

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

与此同时,我将围绕它编写代码,要么使列为空,要么显式地为应用程序代码中的每个insert分配一个(默认'')值…

支持使用表达式作为默认值添加到MySQL 8.0.13,发布于2018-10-22,适用于TEXTJSONBLOBGEOMETRY

你仍然不能写:

create table foo(bar text default 'baz')

但是你现在可以这样写:

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

它们的作用是一样的。

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

有人说,出现错误是因为操作系统- Windows-Linux;但这与操作系统没有直接关系。(但是,MySQL在不同操作系统的不同安装程序中的默认设置可能有所不同,我不确定。)

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

因此,为了防止此错误,可以从MySQL的sql_mode设置中删除STRICT_TRANS_TABLES。(如果影响对数据库的其他操作,则需要将模式重置为之前的值。)

根据MySQL中的SQL模式文档

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

... 和数据类型默认值的文档

不能为BLOBTEXTGEOMETRYJSON数据类型分配默认值。

... TEXT列不能有默认值,但是如果从sql_mode中删除了STRICT_TRANS_TABLES,那么如果在INSERT语句中没有为TEXT列指定值,MySQL就会插入空字符串''