🪟 第6章 SQL基础
📄 一句话概括

数字常量:1.5e3 是科学计数,0x1F 是十六进制;小数默认按 decimal 处理。 字符串一律单引号;里面含单引号就写两遍——'it''s',不是反斜杠。 日期常量本质还是字符串,只有 yyyyMMdd 和 yyyy-MM-dd 永远安全。

📝 本章和前面几章的关系

常量就是「写死在 SQL 里的值」。 看着最简单,但它是类型推断的源头——长度不对、精度不对、格式不对,八成是常量写错了。 教材 6.2 分四小节:数字、字符串、日期时间、符号常量。 前三节是字面量,第四节其实不是常量——@@VERSION 这类东西会变,只是写法上像常量。 演示里把五种数字写法、四种字符串写法、四种日期写法全跑了一遍, 重点看「写进去之后取出来是什么类型」——这一列最能说明问题。

一、数字常量:五种写法

写法 含义 算出来 类型
123 整数 123 int
-45 负整数 -45 int
3.14 小数 3.14 float
1.5e3 科学计数 1500.0 float
0x1F 十六进制 31 int

科学计数法的 e 不区分大小写,1.5e3 和 1.5E3 一样,都等于 1500。 这个写法在存「毫秒数」「字节数」的时候偶尔会用到,日常很少。 0x1F 这种写法在 SQL Server 里含义不同。 SQL Server 的 0x 前缀表示「二进制常量」, 不是数字——0x1F 是一个长度为 1 字节的二进制值,不是 31。这是方言差异。 小数常量的类型要注意。 标准 SQL 里 3.14 是精确小数(decimal), 不是浮点——所以 SELECT 3.14 * 2 得 6.28,不会有浮点误差。 但存进 float 列之后就变成浮点了。

二、字符串常量:单引号,双写转义

写法 说明 取出来
'hello' 普通字符串 hello
'it''s' 含单引号,写两遍 it's
'中文也行' Unicode 字符 中文也行
'<b>' 尖括号不用转义

字符串一律用单引号。 双引号在标准 SQL 里是「标识符」——包着的是表名、列名,不是字符串。 SQL Server 在 QUOTED_IDENTIFIER 关闭时也把双引号当字符串,但那是历史包袱,别依赖。 含单引号的字符串,把单引号写两遍:'it''s'。 这是标准写法。 反斜杠转义 \' 只在 MySQL 里能用,SQL Server 不认——写过去会被当成两个字符。 尖括号、百分号、下划线都不用转义。 它们只在 LIKE 里有特殊含义,普通字符串里就是普通字符。 这一点跟 XML、HTML 的习惯不同,容易多此一举。 还有个坑:N'中文' 前面那个 N。 SQL Server 里它表示「这是 Unicode 字符串」, 存进 nvarchar 列时要用。不写 N 的话,某些排序规则下中文会变成问号。

三、日期常量:本质是字符串

SQL Server 没有「日期字面量」这种语法, 你写的 '2026-10-31' 就是个字符串,由它自己按格式猜成日期。 这就是为什么格式很重要。

写法 安全吗 原因
'20261031' 永远安全 无分隔符,无歧义
'2026-10-31' 安全 ISO 格式,各家都认
'2026/10/31' 基本安全 部分地区认
'10/11/2026' 危险 月日顺序看语言设置

最后一行是真会出事的。 同一个 '10/11/2026',在美式设置下是 10 月 11 日, 在英式设置下是 11 月 10 日。换台服务器、换个语言,数据就错了一个月。 所以教材建议写 'yyyyMMdd'。 没有分隔符就没有歧义,也不受语言设置影响。 代价是可读性差一点——但正确比好看重要。 带时间的写法也一样。 '2026-10-31T14:30:00' 里的 T 是 ISO 8601 的分隔符, SQL Server 2012 之后认它。在那之前得写空格。

四、符号常量:那几个 @@ 开头的

常量 是什么 本沙箱能跑吗
@@VERSION 版本信息字符串 不能,T-SQL 专有
@@ROWCOUNT 上条语句影响的行数 不能
@@IDENTITY 最后插入的自增值 不能
@@ERROR 上条语句的错误号 不能
CURRENT_TIMESTAMP 当前时间,标准 SQL 能

前四个严格说不是常量,是全局变量——它们会变。 教材归到「符号常量」里,是因为写法上像常量: 不用声明,直接用。 @@ROWCOUNT 有个陷阱:任何一条语句执行后它都会重置。 所以「查一下影响了几行, 再判断要不要回滚」这种写法,中间不能插别的语句——一插就归零了。 CURRENT_TIMESTAMP 是标准 SQL 的,四家都认。 它能当默认值用:DEFAULT CURRENT_TIMESTAMP。 GETDATE() 只有 SQL Server 认。 写跨库脚本时,CURRENT_TIMESTAMP 比 GETDATE() 值钱。这一点跟 6.1 说的是同一个原则: 能用标准的就用标准的。

五、动手 Demo:五种数字、四种字符串、四种日期

这一段把三类字面量各跑一遍,重点看「写进去之后取出来是什么类型」。 最后列那几个 @@ 开头的,并说明哪一个能真跑。

import sqlite3

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

# 数字常量:连类型一起看
for v in (u"123", u"-45", u"3.14", u"1.5e3", u"0x1F"):
    got = c.execute(u"SELECT %s" % v).fetchone()[0]
    print(u"%-8s -> %-8s %s" % (v, got, type(got).__name__))

# 字符串:单引号写两遍就是转义
for v in (u"'it''s'", u"'中文也行'"):
    print(v, u"->", c.execute(u"SELECT %s" % v).fetchone()[0])

# 日期:本质还是字符串
for v in (u"'2026-10-31'", u"'20261031'"):
    print(v, u"->", c.execute(u"SELECT %s" % v).fetchone()[0])

print(u"CURRENT_TIMESTAMP =", c.execute(u"SELECT CURRENT_TIMESTAMP").fetchone()[0])

实跑输出:

--------------------------------------------------------------------
常量:字符串、数字、日期
--------------------------------------------------------------------


  ① 数字常量
  --------------------------------------------------------------------
| 写法 | 含义 | 算出来 | Python 类型 |
|---|---|---|---|
| 123 | 整数 | 123 | int |
| -45 | 负整数 | -45 | int |
| 3.14 | 小数 | 3.14 | float |
| 1.5e3 | 科学计数,等于 1500 | 1500.0 | float |
| 0x1F | 十六进制,等于 31 | 31 | int |

  科学计数法那个 e 不区分大小写,1.5e3 和 1.5E3 一样。
  十六进制在 SQL Server 里不是数字字面量,要用 0x 前缀表示二进制常量——
  这一点各家不一样,写的时候要先确认用的是哪个库。

  ② 字符串常量:单引号,里面的单引号写两遍
  --------------------------------------------------------------------
| 写法 | 说明 | 取出来 |
|---|---|---|
| 'hello' | 普通字符串 | hello |
| 'it''s' | 含单引号,写两遍 | it's |
| '中文也行' | Unicode 字符 | 中文也行 |
| '<b>' | 尖括号不用转义 | <b> |

  字符串一律用单引号,双引号在标准 SQL 里是「标识符」不是字符串。
  SQL Server 里双引号包着的默认也当字符串(QUOTED_IDENTIFIER 关掉时),但这是历史包袱。
  含单引号的字符串写两遍单引号:'it''s'。这是标准写法,不是反斜杠——
  反斜杠转义在 MySQL 里能用,SQL Server 里不行。

  ③ 日期时间常量:本质还是字符串
  --------------------------------------------------------------------
| 写法 | SQLite 存成什么 |
|---|---|
| '2026-10-31' | 2026-10-31 |
| '2026/10/31' | 2026/10/31 |
| '20261031' | 20261031 |
| '2026-10-31 14:30:00' | 2026-10-31 14:30:00 |

  SQL Server 里日期常量也是用单引号包着的字符串,由它自己按格式猜。
  能认的格式有十几种,但只有 yyyy-MM-dd 和 yyyyMMdd 是「永远安全」的——
  因为 '10/11/2026' 这种,在不同语言设置下会被理解成 10 月 11 日或 11 月 10 日。
  教材建议写 'yyyyMMdd',就是因为这种写法没有歧义,也不受语言设置影响。

  ④ 符号常量:那几个 @@ 开头的
  --------------------------------------------------------------------
| 常量 | 是什么 | 本沙箱能跑吗 |
|---|---|---|
| @@VERSION | 版本信息字符串 | 不能,SQL Server 专有 |
| @@ROWCOUNT | 上一条语句影响的行数 | 不能 |
| @@IDENTITY | 最后一次插入的自增值 | 不能 |
| @@ERROR | 上一条语句的错误号 | 不能 |
| CURRENT_TIMESTAMP | 当前时间,标准 SQL | 能 |

  CURRENT_TIMESTAMP = 2026-10-30 18:47:09
  前四个是 T-SQL 的全局变量,严格说不是「常量」——它们会变。
  教材把它们归到「符号常量」里,是因为写法上像常量(不需要声明,直接用)。
  最后那个 CURRENT_TIMESTAMP 是标准 SQL 的,四家都认,优先用它。

第一段那张表的最后一列最有信息量:0x1F 在这里是整数 31,而在 SQL Server 里是二进制。 同一个字面量、两种含义,这是方言差异里最难察觉的一种。 1.5e3 算出来是 1500.0,类型是 float。 科学计数法一律按浮点处理——所以别用它写金额, 1.5e3 存进 decimal 列会先变成浮点再转,可能带误差。 第二段两行是字符串的重点:'it''s' 取出 it's。 两个单引号变成一个, 这是标准 SQL 的转义方式。反斜杠那种写法在这里行不通。 第三段其实什么也没干——两个日期都原样存成了字符串。因为 SQLite 没有日期类型。 SQL Server 会把它转成 datetime,而转换的结果取决于格式——这就是为什么 'yyyyMMdd' 最安全。

30 秒实验

  1. 在你的库上执行 SELECT '2026-10-31' + 1(SQL Server),看它返回什么。 它会返回 2026-11-01——日期加一天。 这说明它真把那个字符串当日期解析了。 加一天这件事本身,就是「它认出了这是日期」的证据。
  2. 再执行 SELECT '20261031' + 1,结果应该一样。这就是「无分隔符最安全」的直接证据。
  3. 想一想:为什么 N'中文' 里的 N 不能省? 因为它告诉 SQL Server「这是 Unicode 字符串」。 省略之后按当前代码页解析, 某些排序规则下中文会变成问号。

常见误区

  1. 「字符串可以用双引号」。标准 SQL 里双引号是标识符——包着的是表名列名。 用双引号包字符串,在某些设置下会被当成列名,然后报「列名无效」。一律用单引号。
  2. 「日期写 '2026/10/31' 也能认」。能认,但依赖服务器的语言设置。 换台机器就可能报错或者解析成别的日期。写 '20261031' 或 '2026-10-31',这两种永远安全。
  3. 「@@ROWCOUNT 就是「上一条语句」的行数」。字面上没错,但中间任何一条语句都会重置它, 包括 SELECT @@ROWCOUNT 自己。所以要立刻存到自己的变量里。
自测三问(点开对答案) 1. `'it''s'` 取出来是什么? —— **`it's`。** 两个连续的单引号在字符串里表示一个单引号——这是标准 SQL 的转义方式。 反斜杠 `\'` 那种写法只有 MySQL 认,SQL Server 不认。 2. 为什么日期常量推荐写 `'20261031'`? —— **因为无分隔符就无歧义。** `'10/11/2026'` 这种写法, 不同语言设置下会被理解成10 月 11 日或 11 月 10 日——**换台服务器数据就错了一个月。** 3. `0x1F` 在 SQLite 和 SQL Server 里分别是什么? —— **SQLite 里是整数 31;SQL Server 里是二进制常量(1 个字节的值 0x1F)。**同一个写法、 两种含义——**这种差异不会报错,只会让结果不对。**

下一篇 6.3 讲变量。 DECLARE 声明、SET 赋值、GO 一分割作用域就断——这是长脚本里「变量未声明」报错的根源。

相关笔记