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

CAST 是标准写法,四家都认;CONVERT 是 SQL Server 扩展,多一个样式码参数。 隐式转换是最大的坑:SQL Server 遇到 'abc' 转 INT 会报错,SQLite 直接给 0。 日期转字符串用样式码 112(yyyymmdd)最安全,没有分隔符就不受语言设置影响。

📝 本章和前面几章的关系

转换函数解决的是「类型对不上」的问题。 字段是 varchar,要按数字排序;日期存成了字符串,要按日期比——这些都要转换。 教材 7.4 只有三小节:CAST、CONVERT、两种写法的差别。 节短,但第三小节是重点——它讲的是「什么时候必须用 CONVERT」。 演示里跑了五组「字符串转整数」, 看 SQLite 怎么把 'abc' 变成 0——同样的操作在 SQL Server 里会直接报错, 这是两边差别最大的一处。

一、两种写法,各管一段

来源 写法 谁认
标准 SQL CAST(表达式 AS 类型) 四家都认
SQL Server CONVERT(类型, 表达式, 样式码) 只有它认

能跨库的只有 CAST。 所以「数据本身」的类型转换一律用它——转成 INT、VARCHAR、DECIMAL, 这些都不涉及格式,CAST 够了。 CONVERT 多出来的第三个参数叫「样式码」,专门控制日期字符串的格式。 这是 CAST 做不到的事——CAST(日期 AS VARCHAR) 出来的格式由服务器设置决定,你控不了。 所以分工很清楚: 数值和类型转换用 CAST,日期转成给人看的字符串用 CONVERT 加样式码。 这一条就是教材第三小节说的「两种写法的差别」。

二、隐式转换:最大的坑

「隐式转换」是指不写 CAST,数据库自己转。 比如 WHERE varchar列 = 123, 它会试着把 varchar 转成数字,或者把 123 转成字符串。

值 SQLite 转 INT SQL Server 转 INT
'123' 123 123
'12.5' 12 报错(要转 DECIMAL)
'abc' 0 报错
'12abc' 12 报错
'' 0 报错

后三行是本质差别。 SQLite 是「能转多少算多少」,转不了就给 0;SQL Server 是「转不了就报错」。 哪种好? 从数据质量看,报错更好——它逼你在写入前把数据弄干净。 从「不想被中断」看,给 0 更顺——但那个 0 会静静躺在表里,直到有人算平均值。 所以本课的立场是:别依赖隐式转换。 该写 CAST 就写, 写清楚「我要转成什么」,比让数据库猜安全。 还有一个更隐蔽的坑:隐式转换会让索引失效。WHERE varchar列 = 123 里, 列被转成了数字——索引是按字符串建的,用不上了。 正确写法是 WHERE varchar列 = '123',让常量去转换。

三、日期样式码

样式码 格式 例子
101 mm/dd/yyyy 10/31/2026
102 yy.mm.dd 26.10.31
103 dd/mm/yyyy 31/10/2026
112 yyyymmdd 20261031
120 yyyy-mm-dd hh:mi:ss 2026-10-31 14:30:00
126 ISO 8601 2026-10-31T14:30:00

112 那个 yyyymmdd 最值得记。 它没有分隔符, 不受语言设置影响——所以「把日期转成字符串发给别的系统」时,一般就用它。 126 是 ISO 8601,带 T 分隔符。 给程序解析最省事, JavaScript、Python 的日期解析器都认这个格式。 101、103 这两种「带斜杠」的格式要少用。它们跟 '10/11/2026' 那个坑是一回事—— 同一个字符串在不同地区是不同日期。 样式码只有 CONVERT 认,CAST 没有这个参数。所以「要把日期转成 20261031 这种格式」这件事, 只能用 CONVERT——这就是「什么时候必须用 CONVERT」的答案。

四、转换失败的两种处理

写法 行为 什么时候用
CAST(x AS INT) 失败就报错 数据干净时
TRY_CAST(x AS INT) 失败给 NULL 数据脏、不能中断时
TRY_CONVERT(INT, x) 同上 要样式码时

TRY_CAST 和 TRY_CONVERT 是 SQL Server 2012 加的。它们把「报错」变成「给 NULL」—— 适合「先跑通、再清洗」的场景。 但 NULL 比 0 更隐蔽。 0 至少是个数,参与计算还能发现不对; NULL 参与计算会传染——NULL + 1 还是 NULL,一路传到结果里。 所以推荐的做法是: 先用 TRY_CAST 找出转不了的行(WHERE TRY_CAST(...) IS NULL), 把它们清干净,再用 CAST。一步变两步,但数据是干净的。 SQLite 没有 TRY_CAST。 它的 CAST 本来就不报错, 所以不需要——这又回到那个差别:不报错不等于更安全。

五、动手 Demo:隐式转换与样式码

这一段先对比 CAST 和 CONVERT 两种写法,再跑五组「字符串转整数」看 SQLite 怎么处理脏数据, 最后列日期样式码,说明为什么 112 最安全。

import sqlite3

c = sqlite3.connect(u":memory:")

# 脏数据转整数:SQLite 是「能转多少算多少」
for v in ('123', '12.5', 'abc', '12abc', ''):
    n = c.execute(u"SELECT CAST(? AS INTEGER)", (v,)).fetchone()[0]
    print(repr(v), u"-> ", n)

# 日期转字符串:CAST 控不了格式
d = c.execute(u"SELECT date('2026-10-31')").fetchone()[0]
print(u"原样:", d)
print(u"去横杠:", c.execute(
      u"SELECT replace(date('2026-10-31'), '-', '')").fetchone()[0])

实跑输出:

--------------------------------------------------------------------
CAST 与 CONVERT
--------------------------------------------------------------------


  ① 两种写法,同一个目的
  --------------------------------------------------------------------
| 来源 | 写法 | 谁认 |
|---|---|---|
| 标准 SQL | CAST(表达式 AS 类型) | 四家都认 |
| SQL Server | CONVERT(类型, 表达式, 样式码) | 只有 SQL Server 认 |

  CAST 是标准,CONVERT 是 SQL Server 的扩展。
  CONVERT 多一个「样式码」参数,专门控制日期字符串的格式——这是 CAST 做不到的。

  ② 隐式转换:'abc' 转成数字是 0
  --------------------------------------------------------------------
| 值 | CAST 成 INT |
|---|---|
| '123' | 123 |
| '12.5' | 12 |
| 'abc' | 0 |
| '12abc' | 12 |
| '' | 0 |

  abc 和空字符串都转成了 0,12abc 转成了 12——SQLite 是「能转多少算多少」。
  SQL Server 完全不同:'abc' 转 INT 直接报错「将 varchar 转换为 int 时失败」。
  所以「转换失败当 0」这个行为是 SQLite 的,别拿它去推 SQL Server。
  反过来说,SQL Server 报错是好事——它逼你在写入前把数据弄干净。

  ③ 日期格式化:样式码 101 和 112
  --------------------------------------------------------------------
| 样式码 | 格式 | 例子 |
|---|---|---|
| 101 | mm/dd/yyyy | 10/31/2026 |
| 102 | yy.mm.dd | 26.10.31 |
| 103 | dd/mm/yyyy | 31/10/2026 |
| 110 | mm-dd-yyyy | 10-31-2026 |
| 112 | yyyymmdd | 20261031 |
| 120 | yyyy-mm-dd hh:mi:ss | 2026-10-31 14:30:00 |
| 126 | ISO8601 | 2026-10-31T14:30:00 |

  这些样式码只有 CONVERT 认,CAST 没有这个参数。
  112 那个 yyyymmdd 最值得记——它没有分隔符,不受语言设置影响,
  所以「把日期转成字符串发给别的系统」时,一般就用它。
  126 是 ISO 8601 格式,带 T 分隔符,给程序解析最省事。

  ④ 转换的三种写法对比
  --------------------------------------------------------------------
| 写法 | 说明 | 可移植性 |
|---|---|---|
| CAST(x AS INT) | 标准写法 | 跨库可用 |
| CONVERT(INT, x) | SQL Server 简写 | 不跨库 |
| CONVERT(VARCHAR, d, 112) | 带样式码 | 只有它能控格式 |

  能跨库的只有第一种。所以「数据本身」的转换用 CAST,「给人看的格式」用 CONVERT。
  这个分法很实用:数值、类型转换不涉及格式,用 CAST 就够了;
  日期转成 20261031 这种给人看的字符串,就必须用 CONVERT 加样式码。
  教材 7.4 的第三小节「两种写法的差别」讲的就是这件事。

第一段五行是本节最该看的:'abc' 转成了 0,'12abc' 转成了 12。 这两行在 SQL Server 里都会报错——所以别拿 SQLite 的行为去推 SQL Server。 「能转多少算多少」看着友好,实际很危险。 那个 0 会静静躺在表里, 直到有人算平均值——然后平均分莫名其妙地低。 第二段说明 CAST 的局限:它只能换类型,控不了格式。SQLite 里没有 CONVERT, 所以要得到 20261031,只能用 replace 把横杠去掉——这是个变通写法,不是标准做法。 真实 SQL Server 里一句 CONVERT(VARCHAR, 日期, 112) 就够了。这就是「样式码」的价值—— 它把格式的控制权交给你,而不是交给服务器设置。

30 秒实验

  1. 在你的库上跑 SELECT CAST('abc' AS INT)。 SQL Server 会报「将 varchar 转换为 int 时失败」,SQLite 给 0——两边完全不同。
  2. 跑 SELECT CONVERT(VARCHAR, GETDATE(), 112) 和 CONVERT(VARCHAR, GETDATE(), 120)。 一个得 20261031,一个得 2026-10-31 14:30:00——这就是样式码的作用。
  3. 想一想:为什么 WHERE varchar列 = 123 会让索引失效? 因为列被隐式转成了数字,而索引是按字符串建的。 写成 WHERE varchar列 = '123',让常量去转换,索引就能用。

常见误区

  1. 「CAST 和 CONVERT 差不多,随便用」。CONVERT 多一个样式码, 能控日期格式——这是 CAST 做不到的。而且 CONVERT 只有 SQL Server 认,不跨库。
  2. 「转换失败给 0 更安全,不会中断程序」。0 会静静躺在表里,直到有人算平均值。 NULL 更糟——它会一路传染。 正确做法是先把脏数据找出来清掉。
  3. 「WHERE 字符串列 = 123 和 = '123' 一样」。结果可能一样, 但后者能用索引,前者不能——因为前者把列转成了数字,索引失效。
自测三问(点开对答案) 1. 什么时候必须用 `CONVERT` 而不能用 `CAST`? —— **需要控制日期转字符串的格式时。** `CONVERT` 有样式码参数, `CAST` 出来的格式由服务器设置决定——**你控不了。** 2. `'abc'` 转 `INT`,SQLite 和 SQL Server 各给什么? —— **SQLite 给 0,SQL Server 报错。** SQLite 是「能转多少算多少」, SQL Server 是「转不了就报错」——**从数据质量看,报错更好。** 3. 为什么 `WHERE 字符串列 = 123` 会让索引失效? —— **因为它把列隐式转成了数字,而索引是按字符串建的。** **改成 `WHERE 字符串列 = '123'`,让常量去转换,索引就能用上。**

下一篇 7.5 讲系统函数。 这类函数不处理数据,只回答「我在哪儿」——当前库叫什么、我是谁、从哪连过来的。 排查问题时它们比任何规范都实在。

相关笔记