🪟 第7章 SQL函数的使用
📄 一句话概括

十二个函数里只有六个名字一样;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 秒实验

  1. 跑一次 SELECT CHARINDEX('a', 'abc')(SQL Server)和 SELECT instr('abc', 'a')(SQLite)。 两个都返回 1,但参数顺序是反的——这就是最容易记混的地方。
  2. 造一个 'abc '(尾部三个空格),分别跑 LEN 和 DATALENGTH。 一个得 3、一个得 6——这就是「判断空字符串」为什么不能只看 LEN。
  3. 想一想:为什么参数顺序反了不报错? 因为两个参数都是字符串,类型对得上。 编译器没法判断你是「找」还是「被找」—— 只有结果不对,而且只是返回 0。

常见误区

  1. 「CHARINDEX 和 instr 写法一样,换个名字就行」。参数顺序是反的。 反了不报错,只是永远返回 0。 这个坑几乎人人都踩过。
  2. 「LEN 就是字符串长度」。它去掉尾部空格再数。 LEN('abc ') 得 3,不是 6。要数实际长度得用 DATALENGTH。
  3. 「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 小时,跨零点就差一天。

相关笔记