死钻牛角尖之 模式匹配中 LIKE 子句转义符的使用

shuixin13 2003-05-06 07:53:28
死钻牛角尖之 模式匹配中 LIKE 子句转义符的使用


因为 MySQL 在字符串中使用的是 C 的转义句法(例如 “\n”),
所以在 LIKE 字符串中使用的任何一个 “\” 必须被双写。
例如,为了查找 “\n”,必须以 “\\n” 形式指定它。为了查找 “\”,必须指定它为 “\\\\”
(反斜线被语法分析器剥离一次,另一次在模式匹配时完成,留下一条单独的反斜线被匹配)。

示例(译者注):


CREATE TABLE `ta` (
`id` int(3) unsigned NOT NULL,
`memo` char(6) default NULL,
PRIMARY KEY (`id`)
) TYPE=MyISAM;


INSERT INTO `ta` VALUES("1", "a\\");
INSERT INTO `ta` VALUES("2", "a\\\\");
INSERT INTO `ta` VALUES("3", "a\\n");
INSERT INTO `ta` VALUES("4", "a\n");
INSERT INTO `ta` VALUES("5", "a\\%");


mysql> SELECT * FROM `ta`;
+----+------+
| id | memo |
+----+------+
| 1 | a\ |
| 2 | a\\ |
| 3 | a\n |
| 4 | a
|
| 5 | a\% |
+----+------+

mysql> SELECT * FROM `ta` WHERE `memo` = 'a\\';
mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\';
+----+------+
| id | memo |
+----+------+
| 1 | a\ |
+----+------+
# 在 LIKE 子句中,为了查找 “\”,必须指定它为 “\\\\”


mysql> SELECT * FROM `ta` WHERE `memo` = 'a\\\\';
mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\\\';
+----+------+
| id | memo |
+----+------+
| 2 | a\\ |
+----+------+
# 在 LIKE 子句中,为了查找 “\”,必须指定它为 “\\\\”


mysql> SELECT * FROM `ta` WHERE `memo` = 'a\\n';
mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\n';
+----+------+
| id | memo |
+----+------+
| 3 | a\n |
+----+------+
# 在 LIKE 子句中,为了查找 “\”,必须指定它为 “\\\\”


=========================================================

# 通过以上三个示例我的理解为 “\\\\”在 LIKE 子句中用于匹配一个“\”字符
# (反斜线被语法分析器剥离一次,另一次在模式匹配时完成,留下一条单独的反斜线被匹配)
# 'a\\\\n' 被剥离了两个反斜线后为“a\\n”,
# 字串中的第一个反斜线是转义符,对第二个反斜线进行转义

=========================================================


mysql> SELECT * FROM `ta` WHERE `memo` = 'a\n';
mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\n';
+----+------+
| id | memo |
+----+------+
| 4 | a
|
+----+------+
# 'a\\\n' 经两次剥离后为“a\n”,字串中的反斜线是一个转义符。
# 字串 “a\n” 匹配字串 ASCII(0x610A)

mysql> SELECT * FROM `ta` WHERE `memo` = 'a\\%';
mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\%';
+----+------+
| id | memo |
+----+------+
| 5 | a\% |
+----+------+
# 'a\\\\\%' 经两次剥离后为“a\\\%”,字串中的第一个和第三个反斜线是转义符
# 分别转义一个 “\” 和 换行符,字串匹配 “a\%”。


===========================================================

# 我的疑惑:
# 手册中提到:
# 为了查找 “\n”,必须以 “\\n” 形式指定它。(to search for `\n', specify it as `\\n')
# 原文中的这个 “\n” 是什么?一个换行符(应该用 “\\\n” 表示)?
# 两个字符(也应该用 “a\\\\n” 表示)??


# 就此如止,手册中对此描述还不算有什么太大的问题,
# (to search for `\n', specify it as `\\n'),
# (must double any `\' that you use in your LIKE strings)
# 我们假设这两个描述只不过是一个笔误!
# 下面还有更令人惊讶的!

===========================================================



mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\';
mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\';
+----+------+
| id | memo |
+----+------+
| 1 | a\ |
+----+------+
# 串 “a\\\\” 经两次剥离一次转义后匹配 “a\”
# 而 “a\\” 呢,第一个反斜线就是转义?该字串没有经两次剥离??

mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\\\';
mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\';
+----+------+
| id | memo |
+----+------+
| 2 | a\\ |
+----+------+
# 同样的,串 “a\\\\\\” 的前四个反斜线经两次剥离一次转义后匹配 “a\”
# 而后两个反斜线呢?怎么匹配到一个 “\” 了?


mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\n';
mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\n';
+----+------+
| id | memo |
+----+------+
| 3 | a\n |
+----+------+
# 同样的,串 “a\\\\\\n” 的前四个反斜线经两次剥离一次转义后匹配 “a\”
# 而后两个反斜线呢?被剥离了??


mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\n';
mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\n';
+----+------+
| id | memo |
+----+------+
| 4 | a
|
+----+------+
# 字串 “a\n” 没有经任何剥离?直接转义为一个换行符??


mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\%';
mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\%';
+----+------+
| id | memo |
+----+------+
| 1 | a\ |
| 2 | a\\ |
| 3 | a\n |
| 5 | a\% |
+----+------+
# 既然 'a\\\n' 可以匹配 ASCII(0x610A)
# 为什么 'a\\\%' 就不是匹配 “a%” 而是匹配 “a\”+任意字符 呢


mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\%';
mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\%';
+----+------+
| id | memo |
+----+------+
| 5 | a\% |
+----+------+
1 row in set (0.00 sec)

mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\\%';
mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\\\%';
+----+------+
| id | memo |
+----+------+
| 2 | a\\ |
+----+------+
1 row in set (0.00 sec)


* 以上结果在 MySQL 3.23.56 、MySQL 4.0.12 、MySQL 4.1 中测试通过

示例结束(译者注):





use test;
tee d:/test.txt;

CREATE TABLE `ta` (
`id` int(3) unsigned NOT NULL,
`memo` char(6) default NULL,
PRIMARY KEY (`id`)
) TYPE=MyISAM;
INSERT INTO `ta` VALUES("1", "a\\");
INSERT INTO `ta` VALUES("2", "a\\\\");
INSERT INTO `ta` VALUES("3", "a\\n");
INSERT INTO `ta` VALUES("4", "a\n");
INSERT INTO `ta` VALUES("5", "a\\%");


SELECT * FROM `ta`;
SELECT * FROM `ta` WHERE `memo` = 'a\\';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\';
SELECT * FROM `ta` WHERE `memo` = 'a\\\\';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\\\';
SELECT * FROM `ta` WHERE `memo` = 'a\\n';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\n';
SELECT * FROM `ta` WHERE `memo` = 'a\n';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\n';
SELECT * FROM `ta` WHERE `memo` = 'a\\%';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\%';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\\\';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\n';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\n';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\n';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\n';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\%';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\%';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\%';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\%';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\\%';
SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\\\%';
...全文
125 32 打赏 收藏 转发到动态 举报
写回复
用AI写文章
32 条回复
切换为时间正序
请发表友善的回复…
发表回复
Arbow 2003-05-28
  • 打赏
  • 举报
回复
好好学习~~
swotcoder 2003-05-27
  • 打赏
  • 举报
回复
这个MySQL在转移这部分存在的最大问题就是无法分清是一个NULL字符还是查询中有两个(","")空格。 ^_^
swotcoder 2003-05-22
  • 打赏
  • 举报
回复
字符转移是递归处理的,看看:
原始:a\\\\n
第一次 a\\n
第二次 a\n
又如:a\\\\\\n
第一次 a\\\\n
第二次 a\\n
第三次 a\n

直到\没有可以匹配的转移字符为止。\\,\\\\这种情况会不停的转是因此\这个字符的二进制编码造成的。你可以说是bug,但是就是那么回事。
sesamepaste 2003-05-22
  • 打赏
  • 举报
回复
-------------------------------------
由于 id为3的记录memo的数据为: a\\n
原问题简化为检验表达式值的真假:
select 'a\\n' like 'a\\\\\\n'
此时显示的结果为1,与SQL语句select * from ta where memo like 'a\\\\\\n'的结果相符
-------------------------------------
select 'a \\ n' like 'a \\\\\\ n'
结果: 1

select 'a \ \ n' like 'a \\\ \\\ n'
结果: 1
-------------------------------------
现在字符串'a\\\\\\n'被分为4部分,取其中之一:
select '\ ' like '\\\ '
结果: 1

-------------------------------------
select '\a'
结果: "a"
反斜杠与后面的字符组合如没有特殊的含义,处理的结果为后面的字符

select '\\\a'
结果: "\a"
如果有特殊含义,就转化成所定义的值

-------------------------------------
select '\a' like '\\\a'
结果: 1

select '\a' like '\\\\a'
结果: 1

select '\\\a' like '\\\\a'
结果: 1

但是
select '\a' like '\\\\a'
结果: 0

MySql Reference中String Comparison Functions写到:
Note: Because MySQL uses the C escape syntax in strings (for example, `\n'), you must double any `\' that you use in your LIKE strings. For example, to search for `\n', specify it as `\\n'. To search for `\', specify it as `\\\\' (the backslashes are stripped once by the parser and another time when the pattern match is done, leaving a single backslash to be matched).

select '\\\\a' like '\\\\a'
结果: 0
当"\"小于4个时候进行处理,大于等于4个可能会算作异常(估计)

具体是怎么处理的我也晕了,看看源代码就好了
shuixin13 2003-05-21
  • 打赏
  • 举报
回复
TO sesamepaste(芝麻酱)

mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\n';
+----+------+
| id | memo |
+----+------+
| 3 | a\n |
+----+------+

那下面的作何解??

mysql> SELECT * FROM `ta` WHERE `memo` LIKE 'a\\\\\\n';
+----+------+
| id | memo |
+----+------+
| 3 | a\n |
+----+------+
sesamepaste 2003-05-21
  • 打赏
  • 举报
回复
---------------------------------------
select * from ta where id=3
此时显示的是
id: 3 memo: a\n
此时数据库中元数据是
id: 3 memo: a\\n
---------------------------------------
select * from ta where memo='a\\n'
此时显示的是
id: 3 memo: a\n
数据库在操作时把a\\n<不经处理>地同元数据比较,找到这个记录

select * from ta where memo='_\\n'
此时显示的是
id: null memo: null

如果有字符串匹配处理操作,上面语句应该出来的是:
id: 3 memo: a\n
而不是:
id: null memo: null

---------------------------------------
select * from ta where memo like 'a\\\\n'
首先对'a\\\\n'进行处理,处理后查找元数据中含有'a\\n'的记录
因此出现的是
id: 3 memo: a\n

select * from ta where memo like 'a_\\n'
结果显示为:
id: 3 memo: a\n
可以证明该条语句事先已对做过处理

处理时将"\\"转化为"\"是合理的,因为有时需要寻找一个诸如单引号之类的特殊字符,就要这样输入:
select * from TABLE where FIELD like 'I\'m'
如果不这样做就会出错

因此MySql在这里没什么错
---------------------------------------

打印的时候出了换行应该是前端程序在使用输出语句的时候碰上了换行符\n
自然而然打出的换行,这里也不算错
因为选择出来的东西可能会是有格式一段文章,没有换行符就会显示的乱七八糟了

shuixin13 2003-05-11
  • 打赏
  • 举报
回复
:)

InnoDB 引擎并不是 MySQL AB 公司自己的,
所以可以在标准统一上有点问题,
相信 MySQL 会做得越来越好,

正如你所述,
知道了它的一些限制,
在应用程序中就可以尽力避免它了

:(
seeku 2003-05-11
  • 打赏
  • 举报
回复
是的,innodb类型,我现在正在考虑下午去买一张ms-sql,好像深圳市面上没有D版的professional的ms-sql,郁闷。

尽管mysql需要还有些缺点,但是如果我们知道这个缺点的存在,就可以通过增加编码量来绕过
这些缺点,总之,mysql作为免费的软件,还真的不错。我还是会继续使用的
seeku 2003-05-11
  • 打赏
  • 举报
回复
3.23.56的windows版本对中文支持不好
bombshell 2003-05-11
  • 打赏
  • 举报
回复
郁闷,看了上面的帖子发现mysql的最新版本很多问题。哎,做项目还得用3.23.56呀。
shuixin13 2003-05-10
  • 打赏
  • 举报
回复
mysql> select name,specification from ware_info where help_code like 'dsj%' or help_code='dsj' ;
Empty set (0.00 sec)

mysql> select name,specification from ware_info where (help_code like 'dsj%') or ( help_code='dsj') ;
Empty set (0.27 sec)
---可以看出,这个or非但没有起作用,还把本该出现的纪录都弄没了



的确是很不可思义的,
这也是在 InnoDB 表中吗??
seeku 2003-05-10
  • 打赏
  • 举报
回复
shuixin13(犬犬(心帆)) ,让你看看更吐血的
mysql> select name,specification from ware_info where help_code='dsj' ;
+--------+---------------+
| name | specification |
+--------+---------------+
| 电视机 | 黑白 |
| 电视机 | 全平 |
| 电视机 | 背投 |
+--------+---------------+
3 rows in set (0.00 sec)

mysql> select name,specification from ware_info where help_code like 'dsj%' or help_code='dsj' ;
Empty set (0.00 sec)

mysql> select name,specification from ware_info where (help_code like 'dsj%') or ( help_code='dsj') ;
Empty set (0.27 sec)
---可以看出,这个or非但没有起作用,还把本该出现的纪录都弄没了
mysql> use test;
Database changed
mysql> select * from tt where a = 'abc' and a like 'abc%';
+------+
| a |
+------+
| abc |
+------+
1 row in set (0.00 sec)

mysql> insert into tt values ('abcd');
Query OK, 1 row affected (0.04 sec)

mysql> select * from tt where a = 'abc' and a like 'abc%';
+------+
| a |
+------+
| abc |
+------+
1 row in set (0.01 sec)

在这个表中,or的作用和上一个表不一样。
mysql> insert into tt values ('abcd');
Query OK, 1 row affected (0.04 sec)

mysql> select * from tt where a = 'abc' and a like 'abc%';
+------+
| a |
+------+
| abc |
+------+
1 row in set (0.01 sec)
可以看出a like 'abc%'没有起作用!
天,我都不知道怎么解释了
我都怀疑我会在一个星期内放弃mysql!
shuixin13 2003-05-10
  • 打赏
  • 举报
回复
呵呵,
必竟 MySQL 还是发展中的版本,可能在各方面处理里还没有完全统一标准
:)
seeku 2003-05-10
  • 打赏
  • 举报
回复
我的版本和你一样,但我使用的是innodb类型,
刚刚我使用myisam,发现没有这种现象。mysql> drop table tt;
Query OK, 0 rows affected (0.07 sec)

mysql> create table tt (a varchar(30)) type = MyISAM;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into tt values ('abc');
Query OK, 1 row affected (0.09 sec)

mysql> select * from tt where a like 'abc%';
+------+
| a |
+------+
| abc |
+------+
1 row in set (0.00 sec)

mysql> alter table tt add index idx_tt_a (a);
Query OK, 1 row affected (0.30 sec)
Records: 1 Duplicates: 0 Warnings: 0

mysql> select * from tt where a like 'abc%';
+------+
| a |
+------+
| abc |
+------+
1 row in set (0.01 sec)

mysql>

shuixin13 2003-05-10
  • 打赏
  • 举报
回复
呵呵,
这是决对不应该的,
我的是 MySQL 4.0.12 没你的现象,
你用的是什么版本??
seeku 2003-05-10
  • 打赏
  • 举报
回复
在实践中我发现了令我更吐血的事情,请看下面
mysql> create table tt (a varchar(30));
Query OK, 0 rows affected (0.05 sec)

mysql> insert into tt values ('abc');
Query OK, 1 row affected (0.33 sec)

mysql> insert into tt values ('abc%');
Query OK, 1 row affected (0.05 sec)

mysql> select *from tt;
+------+
| a |
+------+
| abc |
| abc% |
+------+
2 rows in set (0.00 sec)

mysql> select * from tt where a like 'abc%';
+------+
| a |
+------+
| abc |
| abc% |
+------+
2 rows in set (0.00 sec)

mysql> select * from tt where a like 'ABC%';
+------+
| a |
+------+
| abc |
| abc% |
+------+
2 rows in set (0.00 sec)

mysql> alter table tt add index idx_tt_a (a);
Query OK, 2 rows affected (0.53 sec)
Records: 2 Duplicates: 0 Warnings: 0

mysql> select * from tt where a like 'ABC%';
+------+
| a |
+------+
| abc% |
+------+
1 row in set (0.00 sec)

mysql> select * from tt where a like 'ABC%';
+------+
| a |
+------+
| abc% |
+------+
1 row in set (0.00 sec)

mysql>

从上面可以看出,如果加上了索引,如果使用'ABC%'是不能把'abc'检索出来的。
吐血啊吐血啊
bombshell 2003-05-09
  • 打赏
  • 举报
回复
查资料的结果:
要在串中包括一个引号,有以下三种选择:
1。如果串是使用相同的引号括起来的,那么在串中需要需要引号的地方双写引号即可。
例如:'I can''t
"He said,""I told you so."""
2。如果串是用另外的引号括起来的,则不需要双写相应的引号。
例如:"I can't"
'He said,"I told you so."'
3。用反斜杠方式表示,这种方法不去管用来将串括起来的是单引号还是双引号。
例如:'I can\'t'
"I can\'t"
"He said, \"I told you so.\""
'He said, \"I told you so.\"'

在串的环境中,可以用十六进制常数来制定串值。每对十六进制的数字被看作ASCII代码转换为字符,其结果做用于串。
例如:0x616263作为串时为"abc" 。
bombshell 2003-05-09
  • 打赏
  • 举报
回复
转意列表
\0 NULL(ASCII 0)
\' 但引号
\" 双引号
\b 退格
\n 新行
\r 回车
\t 制表符
\\ 反斜杠
shuixin13 2003-05-09
  • 打赏
  • 举报
回复
seeku(青春之歌)

与你有点同感!
可能是 MySQL 的自由度比较高,
有较多的冗错选特性
seeku 2003-05-09
  • 打赏
  • 举报
回复
感觉mysql对于sql语句的格式定义不够严谨,
比如同时可以使用单引号或者双引号来表示串

在like语句中\的使用十分麻烦,我还没有摸出规律,
基本上可以这么做,对被匹配的串进行转换:
'变成''
\变成\\\\
_变成\_
%变成\%
要按照顺序啊
加载更多回复(12)

56,679

社区成员

发帖
与我相关
我的任务
社区描述
MySQL相关内容讨论专区
社区管理员
  • MySQL
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧