最近从 MySQL 迁移到 GoldenDB,踩过一个坑。之前分享过。再来回顾一下。
事故现场
我们先创建一张表 test_1,SQL 如下:
CREATE TABLE`test_1` (
`id`int(8) NOTNULL AUTO_INCREMENT,
`column1`varchar(1) COLLATE utf8_bin DEFAULTNULL,
`date_time` datetime DEFAULTNULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11DEFAULTCHARSET=utf8 COLLATE=utf8_bin
往 test_1 插入 1 条数据,如下图:
INSERT INTO test_1(column1,date_time)VALUES ('a',NOW());
然后我们创建一张跟 test1 表结构一样的表 test2,
CREATE
TABLE
test_2
LIKE
test_1;
执行下面 SQL,
insert into test_2(column1,date_time) select "column1", now() from test_1;
这条 SQL 并不复杂,从 test1 表查出数据写到 test2 表。但不知道写代码的小伙伴出于什么考虑在 column1 上加了双引号。这个 SQL 在 MySQL(8.0 版本)上执行是没有问题的,但是放到了 GoldenDB 上就报错了,因为双引号包着的字段返回的是 column1 这个字符串,最终字段超长报错(Data too long for column 'column1' at row 1)。
问题根因
如果这个坑直接甩锅给 GoldenDB,并不合适。为什么这么说呢?公司专业的 DBA 大佬给了解答,之所以这个 SQL 在 MySQL 上运行没问题,在 GoldenDB 上就会出错,是因为 ANSI_QUOTES 这个参数配置不一致。ANSI_QUOTES 是 MySQL 数据库中的 SQL 模式选项,用于调整标识符与字符串常量的引用规则,使其符合 ANSI SQL 标准。可以使用下面的 SQL 临时开启:
-- 仅对当前会话开启
SET SESSION sql_mode = 'ANSI_QUOTES';
-- 对之后的所有新连接生效(重启后失效)
SET GLOBAL sql_mode = 'ANSI_QUOTES';
或者修改配置文件开启,这样可以永久生效。
ANSI_QUOTES 介绍
开启
开启 ANSI_QUOTES 后,双引号 (") 被解释为标识符引号,作用与反引号 (`) 相同,用于引用数据库、表、字段等名称。例如下面 SQL 中的 name 被解释为列名,而 my_table 被解释为表名。
SELECT "name" FROM "my_table";
而单引号则被解释为字符串,比如下面 SQL 查找结果返回 name 这个字符串:
SELECT 'name' FROM my_table;
关闭
关闭 ANSI_QUOTES 后(默认关闭),双引号 (") 被解释为字符串引号,与单引号 (') 的作用完全相同。反引号 (`) 被用于引用标识符(如数据库名、表名、字段名)。下面的两个 SQL 返回的都是 name 这个字符串:
SELECT 'name' FROM my_table;
SELECT "name" FROM my_table;
为什么要使用 ansi_quotes 呢?主要有 2 个原因:
1.兼容性和可移植性
如果你的应用程序需要与多种遵循 ANSI SQL 标准的数据库(如 PostgreSQL, Oracle, GoldenDB)保持兼容,启用 ANSI_QUOTES 会很有帮助。这可以统一使用双引号来引用标识符,而不用担心数据库迁移会发生因为引号导致的异常。在信创改造的大背景下,这个参数作用很大。
2.SQL 规范
一些开发团队要求必须遵循 SQL 标准,明确区分单引号用于字符串,双引号用于标识符。这可以使 SQL 的意图更加清晰,减少歧义。
总结
ANSI_QUOTES 是 MySQL 数据库中的 SQL 模式选项,用于调整标识符与字符串常量的引用规则,使其符合 ANSI SQL 标准。这个配置开启,对于 SQL 规范化和数据库迁移很有帮助。