- 文档
- 教程
- 知识库
【免费下载链接】til
:memo: Today I Learned
在 PostgreSQL 中,varchar和text等字符串字面量一律使用单引号(')包裹,这就带来了一个绕不开的问题:当字符串内容本身包含单引号时,该如何书写?本指南以 postgres/escaping-a-quote-in-a-string.md 为核心,完整讲解最基础的单引号转义规则,并结合本仓库中同主题的 TIL 笔记,扩展出E前缀转义、美元引用(dollar quoting)与带标签美元引用三种替代方案。读完本文,你将能在 psql 交互环境与程序化生成的 SQL 中从容处理任何包含单引号、甚至包含$$序列的字符串字面量。
为什么单引号必须被转义
字符串字面量的定界符就是单引号本身。PostgreSQL 在解析select 'what''s up!';时,需要区分"作为定界符的单引号"和"作为内容的单引号"。如果不做任何处理直接写出'what's up!',解析器会把'what'当作完整字符串,随后s up!变成游离文本,最终因找不到配对的定界符而报语法错误。
PostgreSQL 官方给出的转义规则非常朴素:字符串字面量内部的单引号,用另一个单引号来转义。也就是说,把'写成'',两个连续的单引号中,第一个让第二个"脱去定界符身份",从而作为字符内容出现在最终字符串中。
方法一:双单引号转义(核心写法)
这是原文档给出的、也是 PostgreSQL 语义最标准、与standard_conforming_strings行为无关的写法。在 psql 中直接验证:
> select 'what''s up!'; ?column? ------------ what's up!解析过程可以这样理解:字面量以第一个'开始,随后遇到两个连续单引号'',它们被整体解释为一个内容字符',最后一个'负责收尾。本仓库的姊妹笔记 postgres/two-ways-to-escape-a-quote-in-a-string.md 用who's on first?展示了同一个要点:
> select 'who''s on first?'; ?column? ----------------- who's on first? (1 row)双单引号写法的最大优点是无条件可用:无论会话级配置standard_conforming_strings如何设置,它都按固定规则解析,不依赖任何上下文开关。缺点同样明显——当字符串中单引号密集出现时,肉眼很难数清引号配对的层级,例如同时包含it's、John's的文本会写成一长串'',阅读与维护成本较高。
常见的失败形态:未转义时的表现
把单引号直接塞进字符串而不做任何处理,查询不会报出"明确的错误",而是表现为语句迟迟不结束。原因在于 psql 认为字符串还没有闭合,会持续等待你输入闭合引号:
> select 'who's on first?'; ...正如 postgres/two-ways-to-escape-a-quote-in-a-string.md 所描述的,这条查询不会执行,因为它"正在等待你关闭第二组引号"。遇到这种悬挂在续行状态的输入,可以按Ctrl-C中断,再补上转义后重新提交。
方法二:E前缀 + 反斜杠转义
除了双写单引号,postgres/two-ways-to-escape-a-quote-in-a-string.md 还记录了第二种思路:给字符串字面量加上E前缀,使反斜杠转义序列(escape sequence)在字符串内生效:
> select E'who\'s on first?'; ?column? ----------------- who's on first? (1 row)这里E'...'让 PostgreSQL 以"转义字符串"的语义解析字面量,于是\'被解释为单个单引号字符。需要注意:在现代 PostgreSQL 中,standard_conforming_strings默认开启,普通字符串(不带E前缀)中的反斜杠会被当作普通字符处理,此时'who\'s on first?'不会得到你想要的结果;只有显式添加E前缀后反斜杠才具备转义能力。因此E前缀写法适合你确实想用反斜杠体系管理特殊字符的场景,日常写包含单引号的普通文本时,双单引号更直接。
方法三:美元引用(Dollar Quoting)
双单引号"易出错、且在程序化生成 SQL 时不好用",这是本仓库另一篇笔记 postgres/escaping-string-literals-with-dollar-quoting.md 提出的核心痛点。它的解决方案是用$$取代'作为定界符:
> select $$Isn't this even nicer?$$; ?column? ------------------------ Isn't this even nicer?用法就是"把两端的'换成$$",字符串内部的单引号不再需要任何转义。这对两类场景尤其有价值:
- 动态拼接 SQL:在应用代码里用字符串模板拼 SQL 时,双单引号要求拼装逻辑额外感知内容中的引号;美元引用让内容原样穿过拼接层。
- 定义函数/存储过程体:函数体内部几乎必然出现字符串与引号,用
$$...$$包裹 PL/pgSQL 函数体是 PostgreSQL 社区最常见的写法。
进阶:带标签的美元引用
如果字符串内容里碰巧出现了连续两个$符号(比如价格文本"$$$"),裸$$定界符也会冲突。此时可用带标签的美元引用。仓库笔记 postgres/label-dollar-quoted-strings-with-a-tag.md 给出了非常直观的 JSON 示例:
> select $JSON${"name": "Sally's Bistro", "price": "$$$"}$JSON$::jsonb; jsonb -------------------------------------------- {"name": "Sally's Bistro", "price": "$$$"} (1 row) > select $JSON${"name": "Sally's Bistro", "price": "$$$"}$JSON$->'name' as name; name ------------------ "Sally's Bistro" (1 row)这段笔记的要点有三个:
- 标签放在两对
$之间:本例标签为JSON,写作$JSON$ ... $JSON$,既能避开内容中的$$,又能向读者传达"这段字面量代表 JSON"的语义信息; - 标签命名规则:标签遵循与未加引号标识符相同的规则,唯一例外是不能包含美元符号;
- 标签区分大小写:
$JSON$与$json$是不同的定界符,书写时需保持一致。
借助带标签美元引用,第一段 SQL 把整段 JSON 文本(内含单引号Sally's与双美元$$$)直接转型为jsonb而无需思考任何字符需要转义;第二段则进一步证明,转型后的值可以像普通jsonb实体一样使用->运算符访问字段。
四种方案如何选择
| 方案 | 写法示例 | 适用场景 | 注意点 |
|---|---|---|---|
| 双单引号 | 'what''s up!' | 手写少量含单引号的文本 | 引号密集时可读性差 |
E前缀 | E'who\'s on first?' | 需要反斜杠转义体系 | 依赖E前缀显式开启转义语义 |
| 裸美元引用 | $$Isn't this nicer?$$ | 程序化生成 SQL、函数体 | 内容含$$时会冲突 |
| 带标签美元引用 | $JSON${...}$JSON$ | 内容含$$或想表达语义 | 标签不能含$,且区分大小写 |
仓库延伸阅读
围绕"字符串与引号"这一主题,本仓库的 postgres 目录下还有以下可直接对照的 TIL 笔记:
- postgres/escaping-a-quote-in-a-string.md:本文核心——双单引号转义;
- postgres/two-ways-to-escape-a-quote-in-a-string.md:
''与E前缀两种方式对照; - postgres/escaping-string-literals-with-dollar-quoting.md:
$$美元引用入门; - postgres/label-dollar-quoted-strings-with-a-tag.md:带标签美元引用处理含
$$的内容。
- 文档
- 教程
- 知识库
【免费下载链接】til
:memo: Today I Learned
相关推荐
TIL 实战:PostgreSQL 字符串中单引号的两种转义方式(双写引号与 E 转义字符串)
TIL 实战:PostgreSQL 字符串中单引号的两种转义方式(双写引号与 E 转义字符串) 在 PostgreSQL 中,字符串字面量必须用单引号( ' )
文档教程知识库xonsh 子进程字符串完全指南:Python 语义、引号规则与字符串前缀实战
xonsh 子进程字符串完全指南:Python 语义、引号规则与字符串前缀实战 本篇技术指南聚焦 xonsh(Python powered shell)在 子进
开发工具Hugo 模板中的 Interpreted String Literal:双引号字符串的转义语义与实战指南
Hugo 模板中的 Interpreted String Literal:双引号字符串的转义语义与实战指南 导读 interpreted string lite
开发工具前端CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考