十二个函数里只有六个名字一样;CHARINDEX(a,b) 和 instr(b,a) 参数顺序是反的。
参数顺序反了不报错,只是永远返回 0——因为 0 是个合法返回值,看着像「真的没有」。
LEN 不数尾部空格,DATALENGTH 数——所以「判断是不是空字符串」两个都得看。
字符串函数是用得最多的一类。清洗数据、拼报表、做模糊匹配,全靠它们。
教材 7.2 列了十二个:ASCII、CHARINDEX、LEFT、LEN、LOWER、LTRIM、NCHAR、
REPLACE、RIGHT、RTRIM、SPACE、STR。名字一样的只有六个,剩下的全要换。
演示里把十二个函数和 SQLite 的对应写法摆在一起,
重点演两个坑:参数顺序、尾部空格。这两个的共同点还是——不报错,结果不对。
一、十二个函数,SQLite 对应哪一个
| SQL Server | 作用 | SQLite |
|---|---|---|
ASCII(s) |
首字符编码 | unicode(s) |
CHAR(n) |
编码转字符 | char(n) |
CHARINDEX(a,b) |
a 在 b 里第几位 | instr(b,a) |
LEFT(s,n) |
左边 n 个字符 | substr(s,1,n) |
RIGHT(s,n) |
右边 n 个字符 | substr(s,-n) |
LEN(s) |
长度,不算尾空格 | length(s) |
LOWER(s) |
转小写 | lower(s) |
LTRIM / RTRIM |
去左 / 右空格 | 同左 |
REPLACE(s,a,b) |
把 a 换成 b | 同左 |
SPACE(n) |
n 个空格 | 没有,用 printf 拼 |
STR(x) |
数字转字符串 | CAST(x AS TEXT) |
名字完全一样的有六行:CHAR、LOWER、LTRIM、RTRIM、REPLACE,
加上语义一致的 LEN/length 算半个。 剩下六行全要换名字。
SPACE 在 SQLite 里没有对应函数,要用 printf('%*s', n, '') 拼——这个写法很别扭,
但它是唯一能造出 N 个空格的。
STR 和 CAST 的差别在「补空格」上。
SQL Server 的 STR(42) 返回 ' 42'(左边补足 10 个字符),
CAST(42 AS VARCHAR) 返回 '42'。很多人第一次用 STR 就是被这个空格坑的。
二、CHARINDEX 的参数顺序是反的
这是本节最该记住的一条:
| 数据库 | 写法 | 谁在前 |
|---|---|---|
| SQL Server | CHARINDEX(要找的, 在哪找) |
子串在前 |
| SQLite | instr(在哪找, 要找的) |
主串在前 |
instr('hello world', 'world') -- 得 7,正确
instr('world', 'hello world') -- 得 0,参数反了
反了不报错,只是永远返回 0。 这才是麻烦的地方——0 是个合法返回值,意思是「没找到」,
看起来像「数据里真的没有」。
这个坑的典型表现是「明明有,就是查不到」。 你会先去怀疑数据、怀疑编码、
怀疑排序规则——最后才发现是参数写反了。大概每个写 SQL 的人都踩过至少一次。
怎么避免? 记一句口诀:「SQL Server 是「小的在前」,
SQLite 是「大的在前」」——要找的那段总是短的,被找的那段总是长的,看哪段在前就知道对不对。
三、LEN 不数尾部空格
| 写法 | 'abc ' 返回 |
说明 |
|---|---|---|
LEN(s) |
3 | 去掉尾空格再数 |
DATALENGTH(s) |
6 | 数实际字节 |
length(s)(SQLite) |
6 | 连空格一起数 |
LEN 会去掉尾部空格,这是 SQL Server 的行为。 SQLite 的 length 不会——
同一段代码两边结果差 3。
所以「判断是不是空字符串」不能只看 LEN。一串空格 LEN 得 0,
但它确实有内容——过滤条件写成 LEN(列) > 0 会把它当成空值漏掉。
正确的写法是 LEN(列) = 0 AND DATALENGTH(列) = 0。两个都满足才算真的空。
这个写法有点啰嗦,但它是唯一准确的。
顺带说一句:DATALENGTH 数的是字节,不是字符。nvarchar 的每个中文字占 2 字节,
所以 DATALENGTH('你好') 得 4,而 LEN('你好') 得 2。这两个数在算字段长度的时候经常被搞混。
四、LEFT / RIGHT 与 substr
取左边几个字符,两边语法差不多;取右边几个,写法完全不同:
LEFT(s, 3) -- SQL Server,左边 3 个
RIGHT(s, 3) -- SQL Server,右边 3 个
substr(s, 1, 3) -- SQLite,左边 3 个
substr(s, -3) -- SQLite,右边 3 个
SQLite 的 substr 支持负数下标:-3 表示「从右往左数第 3 个开始」。
所以 substr('abcdef', -3) 得 def。
SQL Server 没有负数下标这回事。 要取右边 3 个,
只能写 RIGHT(s, 3)——或者 SUBSTRING(s, LEN(s)-2, 3),但那样遇到短字符串会出错。
迁移的时候这类函数要逐个改。 而且改错了不一定报错——
substr(s, 1, 3) 在 SQL Server 里也认(SUBSTRING 要写全名但参数一样),
所以有些能过、有些不能,最乱。
五、动手 Demo:参数顺序、尾部空格、负数下标
这一段把十二个函数和对应写法摆出来,重点跑三段:CHARINDEX/instr 参数顺序反了会怎样、
LEN 和字节数差多少、substr 的负数下标。
import sqlite3
c = sqlite3.connect(u":memory:")
# 参数顺序反了会怎样
print(c.execute(u"SELECT instr('hello world', 'world')").fetchone()[0]) # 7
print(c.execute(u"SELECT instr('world', 'hello world')").fetchone()[0]) # 0
# 尾部空格:字符数 vs 字节数
for v in ('abc', 'abc ', '你好'):
n = c.execute(u"SELECT length(?)", (v,)).fetchone()[0]
print(repr(v), u"字符", n, u"字节", len(v.encode('utf-8')))
# 负数下标取右边
print(c.execute(u"SELECT substr('abcdef', -3)").fetchone()[0]) # def
print(c.execute(u"SELECT substr('abcdef', 1, 3)").fetchone()[0]) # abc
实跑输出:
--------------------------------------------------------------------
字符串函数与参数顺序
--------------------------------------------------------------------
① 十二个函数,SQLite 对应哪一个
--------------------------------------------------------------------
| SQL Server | 作用 | SQLite | 例子 |
|---|---|---|---|
| ASCII(s) | 首字符编码 | unicode(s) | 65 |
| CHAR(n) | 编码转字符 | char(n) | A |
| CHARINDEX(a,b) | a 在 b 里第几位 | instr(b,a) | 第 4 位 |
| LEFT(s,n) | 左边 n 个字符 | substr(s,1,n) | abcd |
| RIGHT(s,n) | 右边 n 个字符 | substr(s,-n) | wxyz |
| LEN(s) | 长度,不算尾空格 | length(s) | 6 |
| LOWER(s) | 转小写 | lower(s) | abc |
| LTRIM(s) | 去左空格 | ltrim(s) | abc |
| RTRIM(s) | 去右空格 | rtrim(s) | abc |
| REPLACE(s,a,b) | 把 a 换成 b | replace(s,a,b) | 同左 |
| SPACE(n) | n 个空格 | 无(用 printf 补) | |
| STR(x) | 数字转字符串 | CAST(x AS TEXT) | 42 |
十二行里名字完全一样的有六行:ASCII、CHAR、LOWER、LTRIM、RTRIM、REPLACE。
剩下六行全不一样——CHARINDEX/instr、LEFT/substr、RIGHT/substr、LEN/length、
SPACE/无、STR/CAST。所以背函数名没用,得记「这一家叫什么」。
CHARINDEX 和 instr 不只是名字不同,参数顺序是反的——这一条下一段专门演。
SPACE 在 SQLite 里没有对应函数,要用 printf('%*s', n, '') 拼出来。
② CHARINDEX 的参数顺序是反的
--------------------------------------------------------------------
SQL Server:CHARINDEX(要找的, 在哪找) — 子串在前,主串在后
SQLite: instr(在哪找, 要找的) — 主串在前,子串在后
instr('hello world', 'world') = 7
instr('world', 'hello world') = 0 ← 参数反了就是这个结果
第二行返回 0 表示「没找到」。参数写反了不报错,只是永远返回 0。
这类错很难发现——因为 0 是个合法返回值,看起来像「真的没有」。
SQL Server 的 CHARINDEX 也一样:参数反了返回 0,不报错。
③ LEN 不数尾部空格,DATALENGTH 数
--------------------------------------------------------------------
| 值 | length() 字符数 | 字节数 |
|---|---|---:|
| 'abc' | 3 | 3 |
| 'abc ' | 6 | 6 |
| '你好' | 2 | 6 |
SQL Server 的 LEN 会去掉尾部空格:LEN('abc ') 得 3。
DATALENGTH 数实际占的字节:DATALENGTH('abc ') 得 6。
所以「判断字符串是不是空」不能只看 LEN——一串空格 LEN 得 0,但它确实有内容。
要判断得写 LEN(列) = 0 AND DATALENGTH(列) = 0,两个都满足才算空。
④ LEFT / RIGHT 的负数写法
--------------------------------------------------------------------
| 写法 | 结果 |
|---|---|
| substr('abcdef', 3) | cdef |
| substr('abcdef', -3) | def |
SQLite 的 substr 支持负数:-3 表示「从右往左数第 3 个开始」,所以得 def。
SQL Server 的 LEFT/RIGHT 没有负数概念,要写 RIGHT(s, 3)。
所以「取右边 3 个」这件事,两家写法完全不同——迁移的时候要逐个改。
第一段两行是 7 和 0。 7 表示「world 从第 7 个字符开始」,
0 表示「没找到」——参数反了就是这个结果,而且不报错。
注意 SQLite 的 instr 返回的 7 是从 1 开始数的(h 是 1,w 是 7)。
有些语言从 0 开始数——跨语言的时候这一位差一,很容易算错。
第二段三行说明「字符数」和「字节数」是两回事。 'abc ' 是 6 个字符,
'你好' 是 2 个字符但 6 个字节——所以按字节算长度会算错中文。
第三段两行是 substr 的两种用法。 负数下标取右边,
-3 得 def——这个技巧在 SQLite 里很方便,但 SQL Server 没有,迁移时要改成 RIGHT。
30 秒实验
- 跑一次
SELECT CHARINDEX('a', 'abc')(SQL Server)和SELECT instr('abc', 'a')(SQLite)。 两个都返回 1,但参数顺序是反的——这就是最容易记混的地方。 - 造一个
'abc '(尾部三个空格),分别跑LEN和DATALENGTH。 一个得 3、一个得 6——这就是「判断空字符串」为什么不能只看LEN。 - 想一想:为什么参数顺序反了不报错? 因为两个参数都是字符串,类型对得上。 编译器没法判断你是「找」还是「被找」—— 只有结果不对,而且只是返回 0。
常见误区
- 「
CHARINDEX和instr写法一样,换个名字就行」。参数顺序是反的。 反了不报错,只是永远返回 0。 这个坑几乎人人都踩过。 - 「
LEN就是字符串长度」。它去掉尾部空格再数。LEN('abc ')得 3,不是 6。要数实际长度得用DATALENGTH。 - 「
STR()就是数字转字符串」。它会左边补空格到固定宽度(默认 10 个字符)。STR(42)得' 42'——前面九个空格。 想要干净的转换用CAST(42 AS VARCHAR)。
自测三问(点开对答案)
1. `CHARINDEX(a, b)` 和 `instr(b, a)` 哪个参数在前? —— **`CHARINDEX` 是「要找的」在前,`instr` 是「在哪找的」在前——顺序正好相反。**记忆口诀: 「SQL Server 小的在前,SQLite 大的在前」。 2. `LEN('abc ')` 得几?`DATALENGTH('abc ')` 得几? —— **`LEN` 得 3(去掉尾空格),`DATALENGTH` 得 6(实际字节)。**所以判断空字符串要两个都看: `LEN = 0 AND DATALENGTH = 0`。 3. SQLite 里怎么取字符串右边 3 个字符? —— **`substr(s, -3)`。** 负数下标表示「从右往左数」。 SQL Server 没有这个写法,要用 `RIGHT(s, 3)`。下一篇 7.3 讲日期函数。重点是一个反直觉的事实:SQLite 的 datetime('now') 给的是 UTC,
SQL Server 的 GETDATE() 给的是本地时间——用错就差 8 小时,跨零点就差一天。
- ☐ (本篇完,下一篇 7.3 日期和时间函数)