日期时间与时区与文本与排序规则

共 46 题
📑 题目列表 46 题
#
★★★

1. AT TIME ZONE 子句的双向语义,timestamptz 转 timestamp、timestamp 转 timestamptz?

请说明 PostgreSQL 中 AT TIME ZONE 子句的双向语义,即 timestamptz 转 timestamp 与 timestamp 转 timestamptz 分别如何工作?

  • 双向转换的语义差异
  • timestamptz(时间点)转 timestamp(本地时间)
  • timestamp 转 timestamptz(按会话时区解读)

PostgreSQL 的 AT TIME ZONE 有双向语义:timestamptz AT TIME ZONE zone 返回 timestamp,把该时间点转换到指定时区的本地时间(去掉时区标记);timestamp AT TIME ZONE zone 返回 timestamptz,把该"无时区"字面时间按指定时区解读为某个时间点。二者方向相反:前者是"时间点→本地时间",后者是"本地时间→时间点"。结果类型恰好互换。

关键要区分"时间点"(timestamptz,绝对时刻)与"墙钟时间"(timestamp,无时刻)。AT TIME ZONE 是两者之间唯一的转换桥梁,方向决定结果类型。

SELECT '2020-01-01 12:00:00+00'::timestamptz AT TIME ZONE 'Asia/Shanghai'; -- 20:00:00
SELECT '2020-01-01 12:00:00'::timestamp AT TIME ZONE 'Asia/Shanghai';     -- 04:00:00+00
#
★★★

2. MySQL DATETIME 与 TIMESTAMP 的存储差异,以及 32 位时间戳回绕问题对 TIMESTAMP 取值范围的影响?

请说明 MySQL 中 DATETIME 与 TIMESTAMP 的存储差异,以及 32 位时间戳回绕对 TIMESTAMP 取值范围的影响?

  • DATETIME 与 TIMESTAMP 存储与范围
  • TIMESTAMP 基于 UTC 32 位时间戳
  • 2038 年问题

MySQL 的 DATETIME 存储字面日期时间,占 8 字节,范围 1000-01-01 到 9999-12-31;TIMESTAMP 按 UTC 存储自 1970-01-01 起的秒数,占 4 字节,范围受 32 位有符号时间戳限制,即 1970-01-01 到 2038-01-19 03:14:07(2038 年问题)。TIMESTAMP 会自动按会话时区转换显示,其取值范围较窄,不适合存 2038 年之后或 1970 年之前的历史日期。MySQL 5.6.4+ 支持小数秒,DATETIME 与 TIMESTAMP 均可带 fsp。

DATETIME 是"墙钟时间",TIMESTAMP 依赖 32 位时间戳,有 2038 回绕风险。存历史日期或远期日期应选 DATETIME。

#
★★★

3. TIMESTAMP WITH TIME ZONE(timestamptz)与 TIMESTAMP WITHOUT TIME ZONE(timestamp)的根本差异,是否存储时区?

请说明 PostgreSQL 中 timestamptz 与 timestamp 的根本差异,二者是否存储时区信息?

  • timestamptz 存 UTC 时间点
  • timestamp 存字面值
  • 都"不存储时区偏移"

根本差异在于是否表示"时间点":timestamptz(TIMESTAMP WITH TIME ZONE)内部以 UTC 存储一个绝对时刻,不存储会话时区偏移,展示时按会话 TimeZone 转换;timestamp(TIMESTAMP WITHOUT TIME ZONE)存储字面墙钟时间,无时区概念。二者都不"存储时区名字",但 timestamptz 语义上是绝对时刻,timestamp 只是本地字面值。关键:timestamptz 的"时区"只在输入输出时用于解释,不持久化。

常见误区是以为 timestamptz 存了时区偏移。实际它是"UTC 时间点 + 显示时按会话转换",本质是绝对时刻。

#
★★★

4. 时间间隔 INTERVAL 的语法,INTERVAL '1 day'、INTERVAL '1 year 2 months'?

请说明 PostgreSQL 中 INTERVAL 类型的语法,如 INTERVAL '1 day'、INTERVAL '1 year 2 months'?

  • INTERVAL 字面量语法
  • 复合单位
  • 与算术运算

PostgreSQL 的 INTERVAL 用 INTERVAL '1 day'INTERVAL '1 year 2 months' 等字面量表示时间间隔,可组合多个单位(年、月、日、时、分、秒)。也可用 INTERVAL '1-2' YEAR TO MONTHINTERVAL '1 day 02:30:00' 等形式。INTERVAL 支持与日期/时间戳加减运算,如 now() + INTERVAL '1 day'。INTERVAL 内部按"月/日/微秒"三部分存储,这也是月日不可交换的原因。

INTERVAL 是时间段概念,与时间点(timestamp)不同,两者运算得到时间点。复合单位语法是常考点。

SELECT '2020-01-01'::date + INTERVAL '1 year 2 months'; -- 2021-03-01
SELECT now() + INTERVAL '30 minutes';
#
★★★

5. 向 timestamptz 插入夏令时跳变中“不存在的时间”(如春季拨快后的 02:30)会发生什么?秋季拨慢产生的“歧义时间”(02:30 出现两次)如何被解释?

请说明向 timestamptz 插入夏令时跳变中"不存在的时间"(春季拨快后的 02:30)会发生什么,以及秋季拨慢产生的"歧义时间"(02:30 出现两次)如何被解释?

  • DST 春季跳变被推后
  • DST 秋季歧义时间的解释规则
  • PostgreSQL 对歧义时间的处理

在 DST 春季拨快(如 America/New_York 跳过 02:00-03:00)时,向该时区的 timestamptz 插入 02:30 这类"不存在的时间",PostgreSQL 会把偏移较小的时间(即拨快前)作为解释,结果自动映射到实际存在的时刻(如推后 1 小时)。在秋季拨慢时 02:30 出现两次,PostgreSQL 采用拨慢后的偏移(即取较晚出现的一次)来解释。这些规则由时区数据库的 DST 转换表决定:春季跳变中不存在的时间按跳变前的偏移解释(本地显示推后 1 小时),秋季歧义时间则按跳变后的偏移解释(取较晚的一次)。

理解 DST 跳变对绝对时刻的影响能避免按本地时间写入导致的一小时偏差错误,尤其在做定时任务与报表时。

#
★★★

6. timestamptz 列的范围查询为何不受会话 TimeZone 影响索引使用(内部以 UTC 存储比较)?但常量字面量的时区解释为何仍依赖 TimeZone 设置?

请说明 timestamptz 列的范围查询为何不受会话 TimeZone 影响索引使用,但常量字面量的时区解释为何仍依赖 TimeZone 设置?

  • 内部 UTC 存储比较
  • 常量字面量的时区解释
  • 索引与扫描

timestamptz 列内部以 UTC 时间点存储,范围比较(如 ts > '2020-01-01')基于绝对时刻,与会话 TimeZone 无关,因此索引可被稳定使用。但不等号右侧的常量字面量 '2020-01-01' 在无时区标记时,会按会话 TimeZone 解释为时间点,导致不同时区下查询边界不同。正确做法是字面量带时区标记(如 '2020-01-01 00:00:00+00')或使用 AT TIME ZONE,使查询语义与会话无关。

列侧是"已定型的 UTC 时间点",比较不受时区影响;字面量侧是"未定型文本",需按会话时区合理解读——这是影响查询结果与索引的点。

-- 字面量带时区,避免受会话 TimeZone 影响
SELECT * FROM t WHERE ts > '2020-01-01 00:00:00+00'::timestamptz;
#
★★★

7. PostgreSQL 中 SET TIME ZONE 与 ALTER DATABASE SET TIMEZONE 的差异?

请说明 PostgreSQL 中 SET TIME ZONE 与 ALTER DATABASE SET TIMEZONE 的差异?

  • 会话级 vs 数据库级
  • 作用范围
  • 优先级

SET TIME ZONE 是会话级设置,只影响当前连接会话对其 timestamptz 的显示与字面量解释,会话结束即失效,不影响其他会话。ALTER DATABASE ... SET TIMEZONE 是数据库级配置,作为该数据库所有新连接的默认 value,可在数据库层面统一时区。二者是"会话默认值 vs 数据库默认值"的关系,会话级可覆盖数据库级设置。推荐的稳妥做法是应用连接时显式 SET TIME ZONE 或使用 UTC。

理解层级能避免"改了库却看不出效果"的困惑——会话级设置覆盖库级默认。

SET TIME ZONE 'Asia/Shanghai';          -- 仅当前会话
ALTER DATABASE mydb SET timezone='UTC'; -- 数据库默认
#
★★★

8. 为什么 INTERVAL 的月与日加法不可交换(1 月 31 日先加 1 月再加 30 天,与先加 30 天再加 1 月结果不同)?PostgreSQL 如何处理月末溢出(如加 1 月到 1 月 31 日)?

请说明为什么 INTERVAL 的月与日加法不可交换,以及 PostgreSQL 如何处理月末溢出?

  • 月与日算术不可交换
  • 月末溢出规则
  • 加法的顺序性

INTERVAL 的月与日加法不可交换,因为"月"的长度不固定,月末溢出规则使结果依赖运算顺序。例如对 2020 年 1 月 31 日:先加 1 月得 2 月 29 日(2020 年是闰年;目标月没有 31 日,自动截断到该月最后一天),再加 30 天得 3 月 30 日;而先加 30 天得 3 月 1 日,再加 1 月得 4 月 1 日,结果不同。PostgreSQL 处理月末溢出时,若目标月没有对应日,则自动截断到该月最后一天。因此 interval 的加法(如 "1 month 30 days")结果与被加时间点的次序相关。

这是"月日不可交换"的经典陷阱,源于日历月长度不定,做跨月运算时须明确顺序与月处理策略。

SELECT '2020-01-31'::date + INTERVAL '1 month' + INTERVAL '30 days'; -- 2020-03-30
SELECT '2020-01-31'::date + INTERVAL '30 days' + INTERVAL '1 month'; -- 2020-04-01
#
★★★

9. MySQL 中 CONVERT_TZ 函数?

请说明 MySQL 中 CONVERT_TZ 函数的用途与用法?

  • 时区转换函数
  • 参数与依赖时区表
  • 与时区存储配合

MySQL 的 CONVERT_TZ(dt, from_tz, to_tz) 将一个 datetime 从 from_tz 时区转换到 to_tz 时区,返回 datetime。例如 CONVERT_TZ('2020-01-01 12:00:00', '+00:00', '+08:00') 返回 20:00:00。依赖 MySQL 时区表(mysql.time_zone_name),若时区表未加载则只能使用数字偏移。CONVERT_TZ 常用于需要在应用层做时区换算、或把 UTC 存储转本地展示的场景。

CONVERT_TZ 是 MySQL 手动时区换算的工具,与时区表的加载状态相关,是区别于 PostgreSQL AT TIME ZONE 的方言。

SELECT CONVERT_TZ('2020-01-01 12:00:00', '+00:00', '+08:00'); -- 2020-01-01 20:00:00
#
★★★

10. MySQL 中 ON UPDATE CURRENT_TIMESTAMP 的列属性?

请说明 MySQL 中 ON UPDATE CURRENT_TIMESTAMP 列属性的作用与注意事项?

  • 自动更新时间戳
  • 与 DEFAULT 组合
  • 触发条件

MySQL 中,在 TIMESTAMP/DATETIME 列上声明 DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 时,插入时自动填充当前时间,后续任何行更新都会自动把该列更新为当前时间。ON UPDATE 独立于 DEFAULT,可单独使用。注意:仅当行其他列发生变化时触发;若明确将该列设为当前值则不会额外触发。适合记录"最后修改时间"。

这是 MySQL 的自动维护列,类比触发器的简化,重点是与 DEFAULT 的区分和触发时机。

CREATE TABLE t (
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
#
★★★

11. PostgreSQL 中 pg_timezone_names 视图?

请说明 PostgreSQL 中 pg_timezone_names 视图的作用与用法?

  • 时区目录视图
  • 字段结构
  • 查询时区

pg_timezone_names 是 PostgreSQL 的系统视图,列出所有已识别的时区名与属性,字段包括 name(时区名)、abbrev(缩写)、utc_offset(当前 UTC 偏移)、is_dst(是否夏令时)。可用来查询某个时区是否可用、偏移等信息。例如 SELECT * FROM pg_timezone_names WHERE name = 'America/New_York';。它用于校验时区字符串或做时区分析。

它是查询"可用时区与偏移"的目录,区别于 pg_timezone_abbrevs(缩写表)。

SELECT name, utc_offset, is_dst FROM pg_timezone_names WHERE name LIKE 'Asia/%';
#
★★★

12. PostgreSQL 中 transaction_timestamp()、statement_timestamp() 的差异?

请说明 PostgreSQL 中 transaction_timestamp()、statement_timestamp() 的差异?

  • 事务开始时间 vs 语句开始时间
  • 稳定性
  • 实际用途

transaction_timestamp() 返回当前事务开始的时间戳,在事务内所有语句返回相同值;statement_timestamp() 返回当前语句开始的时间戳,同一事务内不同语句可能不同。now() 与 transaction_timestamp() 等价。clock_timestamp() 则返回实时时钟,每次调用都不同。若要保证事务内时间一致(如审计、批量操作),用 transaction_timestamp();若关心单条语句执行时间,用 statement_timestamp()。

三者的区别在于"取时间的时刻":事务开始、语句开始、实时时钟。理解可避免时间戳前后不一致。

SELECT now();                      -- 事务开始时间
SELECT statement_timestamp();      -- 当前语句时间
SELECT clock_timestamp();          -- 实时时钟
#
★★★

13. epoch 转换 FROM_UNIXTIME(MySQL)?

请说明 MySQL 中 FROM_UNIXTIME 函数的作用与用法?

  • epoch 秒转 datetime
  • 参数与返回值
  • 时区处理

MySQL 的 FROM_UNIXTIME(ts) 将 Unix 时间戳(自 1970-01-01 00:00:00 UTC 起的秒数)转换为会话时区的 DATETIME。可带第二参数指定格式,如 FROM_UNIXTIME(ts, '%Y-%m-%d')。反向函数是 UNIX_TIMESTAMP()。由于返回的是会话时区下的本地时间,跨时区应用需注意一致性。PostgreSQL 对应 to_timestamp(ts)

FROM_UNIXTIME 是 epoch 到可读时间的转换入口,格式化参数与会议时区是常考点。

SELECT FROM_UNIXTIME(1577836800); -- 2020-01-01 00:00:00
SELECT FROM_UNIXTIME(1577836800, '%Y-%m-%d %H:%i:%s');
#
★★★

14. MySQL 中 utf8(utf8mb3)与 utf8mb4 的差异,utf8mb4 支持 4 字节(emoji)?

请说明 MySQL 中 utf8(utf8mb3)与 utf8mb4 的差异,以及为什么 utf8mb4 支持 4 字节字符(emoji)?

  • utf8mb3 最多 3 字节
  • utf8mb4 支持 4 字节
  • emoji 与补充平面字符

MySQL 的 utf8(实际是 utf8mb3)是 UTF-8 的 3 字节实现,只能编码 BMP 基本多文种平面内最多 3 字节的字符,无法存储 4 字节的 emoji 或补充平面字符(如部分罕见汉字、emoji)。utf8mb4 是完整 4 字节 UTF-8,可存储包括 emoji 在内的全部 Unicode 字符。因此存储 emoji 必须用 utf8mb4。MySQL 8.0 默认字符集即为 utf8mb4。

这是 emoji 存不进去的经典坑:utf8mb3 的不完整 UTF-8 实现导致 4 字节字符报错,需换 utf8mb4。

#
★★★

15. PostgreSQL 中 COLLATE 子句的覆盖规则,列定义、表定义、查询?

请说明 PostgreSQL 中 COLLATE 子句的覆盖规则,即列定义、表定义、查询三者的优先级?

  • COLLATE 的作用层级
  • 显式 COLLATE 覆盖
  • 继承与默认

PostgreSQL 中排序规则(collation)可在多个层级指定:数据库默认、列定义(COLLATE)、表定义(TABLE COLLATE)、表达式或查询中的显式 COLLATE。覆盖优先级为:表达式/查询中的显式 COLLATE 最高,其次列定义,再次表定义,最后数据库默认。优先级叠加原则是"越具体越优先"。显式 COLLATE 可临时覆盖列默认排序,用于特定比较或排序。

理解覆盖层级能预测"同一列在不同查询中排序结果可能不同"的现象,尤其在大小写敏感/不敏感混用场景。

SELECT name COLLATE "C" FROM users ORDER BY name COLLATE "C";
#
★★

16. PostgreSQL 的 timestamptz 与 Java OffsetDateTime 映射?

请说明 PostgreSQL 的 timestamptz 与 Java OffsetDateTime 的映射关系?

  • JDBC 类型映射
  • OffsetDateTime 与时间点
  • timezone 处理

PostgreSQL 的 timestamptz 表示时间点,与 Java 的 OffsetDateTime(带偏移的日期时间)语义匹配。JDBC 驱动可将 timestamptz 映射为 OffsetDateTime 或 Instant。读取时驱动返回带偏移的时间,通常以会话时区为偏移。Java 侧用 OffsetDateTime 保留偏移语义,用 Instant 表示绝对时刻。注意 PostgreSQL 虽不存储偏移,但 JDBC 返回时会附上会话时区偏移,因此推荐连接时区统一为 UTC。

映射核心是"时间点"语义对齐,避免用 LocalDateTime 接收 timestamptz 导致时刻丢失。

OffsetDateTime odt = rs.getObject("created_at", OffsetDateTime.class);
#
★★

17. PostgreSQL 中 INITDB 时选择的 locale 与 ICU 的差异?

请说明 PostgreSQL 中 initdb 时选择的 locale 与 ICU 的差异?

  • 系统 locale 与 ICU 提供者
  • 排序规则能力差异
  • 版本一致性

PostgreSQL 可通过 initdb 的 --locale 或 --provider 选择排序规则来源:libc(系统 locale,依赖操作系统)或 icu(ICU 库,跨平台一致)。系统 locale 的排序规则随操作系统变化,升级 OS 可能改变排序结果;ICU 提供更丰富、稳定的排序规则(如大小写、地区细粒度),且不依赖 OS。initdb 时选择会决定默认 collation 的能力。现代部署常选用 ICU 以获得跨平台一致的排序行为。

locale 来源影响排序稳定性与可移植性,ICU 是更可控的现代选择。

#
★★

18. TEXT 类型(PostgreSQL)与 VARCHAR 的等价性?

请说明 PostgreSQL 中 TEXT 类型与 VARCHAR 的等价性?

  • 无长度限制的等价
  • 内部存储相同
  • 性能等价

PostgreSQL 中 TEXT 与无长度限制的 VARCHAR(VARCHAR 无参数)在存储与性能上完全等价,VARCHAR(n) 只是额外加了长度约束。三者内部均为 varlena 变长存储,没有性能差异。唯一区别是 VARCHAR(n) 会检查长度并报错,TEXT 与 VARCHAR 无长度限制。因此可用 TEXT 存任意长度文本,选择取决于是否需要长度约束的语义。

与 MySQL 不同(MySQL TEXT 与 VARCHAR 有差异),PostgreSQL 中 TEXT 与 VARCHAR 基本等价,这是重要方言差异。

#
★★

19. VARCHAR(n) 与 CHAR(n) 的存储差异,变长 vs 定长、尾随空格处理?

请说明 VARCHAR(n) 与 CHAR(n) 的存储差异,以及尾随空格的处理?

  • 变长 vs 定长
  • 尾随空格
  • 存储与比较

PostgreSQL 中 CHAR(n) 固定长度,不足 n 时以空格填充(存储层面),比较时忽略尾随空格;VARCHAR(n) 变长,只存实际字符,不填充,比较时尾随空格有效(不会忽略)。MySQL 中 CHAR 定长并截断到 n,VARCHAR 变长并保留尾随空格,且两者比较时都会忽略尾随空格。总体而言,CHAR 因定长在存储与比较上略特殊,VARCHAR 更灵活。现代多数字段用 VARCHAR 或 TEXT。

尾随空格处理是两者差异核心,CHAR 的填充与比较时忽略空格是面试点。

#
★★

20. MySQL 中 SHOW COLLATION 与排序规则的选择?

请说明 MySQL 中 SHOW COLLATION 的用途与排序规则的选择?

  • SHOW COLLATION 查询
  • 排序规则与字符集关系
  • 选择依据

SHOW COLLATION 列出 MySQL 所有可用的排序规则及其所属字符集、是否默认、是否二进制。它用于查看某字符集下可选的排序规则(如 utf8mb4 下的 utf8mb4_general_ci、utf8mb4_unicode_ci、utf8mb4_bin 等)。选择排序规则需考虑大小写/重音敏感性、排序性能与准确性。一般默认 ci(大小写不敏感)即可,需精确匹配用 bin。

排序规则决定字符串比较与排序行为,SHOW COLLATION 是查询与选择它们的入口。

SHOW COLLATION WHERE Charset = 'utf8mb4';
#
★★

21. 字符串索引与排序规则,LIKE 模式匹配的大小写敏感?

请说明字符串索引与排序规则下,LIKE 模式匹配的大小写敏感行为?

  • 排序规则决定大小写敏感
  • LIKE 与索引
  • _ci 与 _bin 差异

LIKE 的大小写敏感性由列或表达式的排序规则决定。在 _ci(大小写不敏感)排序规则下,LIKE 'abc%' 会匹配 'ABC'、'Abc' 等;在 _bin 或 _cs(大小写敏感)排序规则下则区分大小写。若列使用 B-Tree 索引,前缀匹配的 LIKE(如 LIKE 'abc%')可利用索引,但若排序规则与查询的 collation 不一致可能导致索引失效。因此 LIKE 大小写行为取决于 collation,而非语法本身。

LIKE 检索行为紧耦合 collation,前缀 LIKE 可走索引,但 collation 一致性是索引可用前提。

#
★★

22. CHAR 与 VARCHAR 的尾随空格处理?

请说明 CHAR 与 VARCHAR 在尾随空格处理上的差异?

  • CHAR 填充与比较
  • VARCHAR 保留
  • 各数据库差异

PostgreSQL 中 CHAR(n) 固定长度,不足时以空格填充,比较时忽略尾随空格;VARCHAR(n) 存储实际字符、保留尾随空格,比较时尾随空格有效(不会忽略)。MySQL 中 CHAR 存储时截断尾随空格,VARCHAR 保留尾随空格,比较时忽略尾随空格。因此尾随空格的行为存在数据库差异,存储含尾随空格数据的场景需谨慎。

尾随空格是"存储 vs 比较"两层面的处理差异,且 MySQL 与 PostgreSQL 行为不同,是兼容性陷阱。

#
★★

23. CHAR(10) 与 VARCHAR(10) 的存储差异?

请说明 CHAR(10) 与 VARCHAR(10) 的存储差异?

  • 定长 vs 变长
  • 空间占用
  • 使用场景

CHAR(10) 固定分配 10 字符空间,不足时以空格填充,适合长度固定或接近固定的字段(如国家代码、证件号);VARCHAR(10) 只存储实际字符长度,占"实际字符数 + 长度前缀",更省空间,适合长度可变字段。PostgreSQL 中 CHAR(n) 与 VARCHAR(n) 实际都按变长存储,仅 CHAR 有填充语义;MySQL 中 CHAR 定长、VARCHAR 变长,空间差异明显。

存储差异是"定长 vs 变长":CHAR 以固定空间换取简单,VARCHAR 以长度前缀换取灵活。

#
★★

24. MySQL 中 COLLATE utf8mb4_bin 的二进制排序?

请说明 MySQL 中 COLLATE utf8mb4_bin 的二进制排序特性?

  • 二进制排序含义
  • 大小写敏感
  • 与 _ci 对比

utf8mb4_bin 是 utf8mb4 字符集下的二进制排序规则,按字符的 Unicode 码点(二进制编码)直接比较,因此大小写敏感且不做语言化折叠(如不把 'a' 与 'A' 视为相等)。与之相对,utf8mb4_general_ci / unicode_ci 是大小写不敏感的。utf8mb4_bin 适合需要精确字节级比较、作为唯一键或需要区分大小写的场景,但排序可能与语言习惯不同。

_bin 后缀意味着"按二进制字节序比较",区分大小写、无重音折叠,是与 _ci 对立的选择。

#
★★

25. MySQL 中 SHOW CHARACTER SET 的查询?

请说明 MySQL 中 SHOW CHARACTER SET 查询的用途?

  • 列出字符集
  • 默认 collation
  • 字符集管理

SHOW CHARACTER SET 列出 MySQL 支持的所有字符集及其默认排序规则、最大长度。例如 utf8mb4 默认 collation 为 utf8mb4_0900_ai_ci(8.0)。它用于在创建数据库/表时选择字符集、查看字符集属性。也可用 information_schema.CHARACTER_SETS 查询。

这是查看可用字符集元数据的入口,配合 SHOW COLLATION 理解字符集与排序规则的关系。

SHOW CHARACTER SET;
#
★★

26. MySQL 中 utf8mb4_unicode_ci 与 utf8mb4_0900_ai_ci 的差异?

请说明 MySQL 中 utf8mb4_unicode_ci 与 utf8mb4_0900_ai_ci 的差异?

  • 排序规则版本
  • 准确性与性能
  • 各版本默认

utf8mb4_unicode_ci 基于 Unicode 4.0 的 UCA 算法,排序相对准确但速度较慢;utf8mb4_0900_ai_ci 基于 Unicode 9.0,是 MySQL 8.0 的默认 collation,更快更准确,ai 表示不区分重音、ci 表示不区分大小写。0900 系列还支持新的权重与 emoji 处理。迁移时若从旧版 unicode_ci 切换到 0900_ai_ci,排序结果可能变化,需注意兼容性。

0900_ai_ci 是 8.0 默认且更优,但版本差异导致排序结果可能不同,迁移需测试。

#
★★

27. PostgreSQL 中 TEXT 类型是否有性能劣势?

请说明 PostgreSQL 中 TEXT 类型是否有性能劣势?

  • TEXT 与 VARCHAR 等价
  • 存储引擎
  • 性能结论

PostgreSQL 中 TEXT 类型没有性能劣势:它与 VARCHAR 和无长度限制的 varchar 在存储表示、索引、比较上完全一致,底层都是 varlena 变长。TEXT 没有"最大长度限制带来的优化",也没有额外开销。因此可放心使用 TEXT 存储任意文本,性能与 VARCHAR 相同。唯一注意点是 TOAST 对超长文本的压缩与外部存储,但这对 TEXT 与 VARCHAR 同样适用。

与 MySQL 不同,PostgreSQL 的 TEXT 与 VARCHAR 无性能差别,这是常见误区。

#
★★

28. PostgreSQL 中 VARCHAR 的最大长度限制?

请说明 PostgreSQL 中 VARCHAR 的最大长度限制?

  • VARCHAR(n) 的 n 上限
  • 无长度 VARCHAR
  • 内部限制

PostgreSQL 的 VARCHAR(n) 中 n 最大可指定为 10485760(约 10MB),再加上 varlena 的 4 字节长度头,实际最大约 1 GB 的字符串(受块大小限制)。VARCHAR 不带参数时无长度限制,等价于 TEXT。实际存储受 TOAST 与单值大小限制,超出 1GB 无法存储。因此 VARCHAR(n) 的限制主要是人为约束,不是硬性存储上限。

长度上限远大于其他数据库,VARCHAR 无参数即 TEXT,答案取决于 n 的显式上限。

#
★★

29. PostgreSQL 中 database、table、column 级 collation 的覆盖?

请说明 PostgreSQL 中 database、table、column 级 collation 的覆盖规则?

  • 多级 collation
  • 覆盖优先级
  • 继承

PostgreSQL 的 collation 可在 database、table、column 三级指定,加上查询中的显式 COLLATE。覆盖优先级由低到高为:database 默认 < table 级 < column 级 < 表达式/查询显式。列定义中的 COLLATE 覆盖表与库级,查询中的显式 COLLATE 覆盖一切。未指定时逐级继承。理解该链便于预测排序行为与设置迁移。

嵌套覆盖层级的核心是"越具体越优先",列级与查询级覆盖库级默认。

#
★★

30. PostgreSQL 中 pg_collation 视图?

请说明 PostgreSQL 中 pg_collation 视图的作用?

  • 排序规则目录
  • 字段
  • 查询

pg_collation 是 PostgreSQL 的系统目录,列出所有可用的排序规则(collation),包括来自 libc 与 ICU 的。字段包括 collname、collnamespace、collprovider(是否来自 ICU/libc)、collcollate、collctype 等。创建自定义 collation(CREATE COLLATION)后也会出现在这里。可用于查询可用的排序规则与选择。

它是查看与选择排序规则的目录,配合 CREATE COLLATION 管理自定义规则。

SELECT collname, collprovider FROM pg_collation WHERE collprovider = 'i';
#
★★

31. TEXT 与 VARCHAR 的性能差异?

请说明 TEXT 与 VARCHAR 的性能差异?

  • PostgreSQL 中无差异
  • MySQL 中差异
  • 场景

PostgreSQL 中 TEXT 与 VARCHAR 性能无差异(底层变长存储相同)。MySQL 中 TEXT 与 VARCHAR 有差异:VARCHAR 可存于行内并支持索引前缀,TEXT 最大 65535 字节且可能涉及外部存储,但二者在索引与临时表上也有差异。综合而言,PostgreSQL 可用 TEXT 与 VARCHAR 等价;MySQL 选 VARCHAR 作为常规短文本更常见。

性能差异需区分数据库方言:PG 无差异,MySQL 有轻微差异,答案取决于具体数据库与场景。

#
★★

32. CURRENT_TIMESTAMP 与 now() 的语义差异?

请说明 PostgreSQL 中 CURRENT_TIMESTAMP 与 now() 的语义差异?

  • 等价性
  • 事务开始时间
  • 标准函数

PostgreSQL 中 CURRENT_TIMESTAMP(标准 SQL 函数)与 now() 语义等价,都返回当前事务开始时间,且返回类型为 timestamptz。CURRENT_DATE 返回 date,CURRENT_TIME 返回 time with time zone。区分它们主要看返回类型与是否带时区。若需实时时钟用 clock_timestamp()。因此 CURRENT_TIMESTAMP 与 now() 在 PostgreSQL 中无实质差异。

同为事务开始时间,二者等价;区别在于与其他 CURRENT_* 函数及 clock_timestamp 的对比。

#
★★

33. epoch 时间与人类可读时间的转换,EXTRACT(EPOCH FROM ts)?

请说明 PostgreSQL 中 EXTRACT(EPOCH FROM ts) 的用途与 epoch 时间转换?

  • EXTRACT EPOCH
  • 时间点转秒数
  • 反向 to_timestamp

EXTRACT(EPOCH FROM ts) 返回时间点自 1970-01-01 00:00:00 UTC 起的秒数(浮点),是 timestamptz 转 epoch 秒的常用方式。反向转换用 to_timestamp(sec) 把 epoch 秒转回 timestamptz。也可用 date_part('epoch', ts)。它是"人类可读时间"与"机器可读秒数"之间的桥梁,常用于接口、日志、缓存 key。

EXTRACT(EPOCH ...) 与 to_timestamp 是时间点与 epoch 秒的双向转换对。

SELECT EXTRACT(EPOCH FROM '2020-01-01 00:00:00+00'::timestamptz); -- 1577836800
SELECT to_timestamp(1577836800);                                  -- 2020-01-01 00:00:00+00
#
★★

34. 夏令时(DST)的处理,America/New_York 等 DST 时区在 2:30 AM 跳变?

请说明夏令时(DST)时区(如 America/New_York)在 2:30 AM 跳变的处理?

  • DST 跳变规则
  • 不存在与歧义时间
  • 存储与显示

America/New_York 等 DST 时区在春季将时钟拨快 1 小时(2:00 直接跳到 3:00),导致 2:00-3:00 之间不存在;秋季拨慢 1 小时(2:00 回到 1:00),导致 1:00-2:00 出现两次。数据库以 timestamptz 存储绝对时刻,存储不受影响,但把本地时间用户输入转成时间点时会遇到"不存在时间"(推后)与"歧义时间"(取拨慢后的偏移,即较晚出现的一次)。处理时需明确业务规则,或统一用 UTC 存储避免 DST 复杂性。

DST 处理的核心是"本地墙钟时间"与"绝对时刻"的转换歧义,UTC 存储可规避大部分问题。

#
★★

35. TIMESTAMP 字面量的语法?

请说明 PostgreSQL 中 TIMESTAMP 字面量的语法?

  • 字面量写法
  • 类型转换
  • 时区后缀

PostgreSQL 的 timestamp 字面量写法为 TIMESTAMP '2020-01-01 12:00:00'(无时区)或 TIMESTAMPTZ '2020-01-01 12:00:00+00'(带时区偏移)。也可用 '2020-01-01 12:00:00'::timestamptzCAST('2020-01-01' AS timestamp)。日期字面量 '2020-01-01' 会被自动解释为 timestamp。带时区后缀(+08、+00)或时区名决定 timestamptz 的字面量解释。

字面量语法影响类型推断与时区解释,带时区标记的 timestamptz 字面量语义最明确。

SELECT TIMESTAMP '2020-01-01 12:00:00';
SELECT '2020-01-01 12:00:00+08'::timestamptz;
#

36. 存储格式,timestamptz 内部以 UTC 存储,timestamp 存储字面值?

请说明 PostgreSQL 中 timestamptz 与 timestamp 的存储格式差异?

  • timestamptz 存 UTC
  • timestamp 存字面值
  • 会话时区

PostgreSQL 中 timestamptz 内部以 UTC 时间点存储(固定 8 字节),会话时区只影响显示与字面量解释,不改变存储值;timestamp 则存储字面墙钟时间,无时区概念。因此同一时刻在不同会话时区下,timestamptz 显示不同但底层值相同,timestamp 显示恒定。这是两者最根本的存储差异。

"timestamptz 存 UTC、timestamp 存字面值"是理解时区语义的基石。

#

37. 时区数据库(tzdata)的更新与闰秒处理?

请说明 PostgreSQL 时区数据库(tzdata)的更新与闰秒处理?

  • tzdata 更新
  • 闰秒处理
  • 版本升级

PostgreSQL 使用系统或内置的时区数据库(tzdata)来解析 DST 转换与时区偏移。tzdata 随操作系统或 PG 版本更新,若使用旧版可能对近期的 DST 规则变化处理错误。闰秒(leap second)在 POSIX 时间与多数 tzdata 中被忽略或按 59:59 处理,数据库通常不插入闰秒,也不影响常规时间运算。更新 tzdata 需重启数据库实例,且可能改变既有时间戳的显示解释。

tzdata 的新旧直接影响 DST 规则正确性,闰秒在大多应用中可忽略但需知晓。

#

38. CURRENT_DATE 返回日期不含时间?

请说明 PostgreSQL 中 CURRENT_DATE 返回什么?

  • 返回类型 date
  • 不含时间
  • 会话时区

PostgreSQL 的 CURRENT_DATE 返回当前日期(date 类型),不含时间部分,且基于会话时区计算。它与 CURRENT_TIMESTAMP 相比仅返回日期。CURRENT_DATE 在事务内保持不变。若需日期+时间用 CURRENT_TIMESTAMP/now(),若需指定时区的日期用 (now() AT TIME ZONE 'zone')::date。

CURRENT_DATE 返回 date 类型,不含时间,是取"今天"的快捷方式。

#

39. DATE 与 TIMESTAMP 的存储差异?

请说明 PostgreSQL 中 DATE 与 TIMESTAMP 的存储差异?

  • 存储字节
  • 精度
  • 场景

PostgreSQL 中 DATE 存储 4 字节,表示年月日(不含时间),范围 4713 BC 到 5874897 AD;TIMESTAMP 存储 8 字节,含日期与时间(可带微秒),范围更大。DATE 适合生日、账期等只关心日期的字段,TIMESTAMP 适合需要精确时刻的事件。DATE 与 TIMESTAMP 可互相转换(date::timestamp)。

DATE 4 字节、TIMESTAMP 8 字节,精度与空间差异决定用途。

#

40. EXTRACT(YEAR FROM date) 的返回值?

请说明 PostgreSQL 中 EXTRACT(YEAR FROM date) 的返回值?

  • EXTRACT 语法
  • 返回类型
  • 字段

EXTRACT(YEAR FROM date) 从日期中提取年份,返回 numeric 类型(如 2020)。EXTRACT 可提取 year、month、day、hour、minute、second、dow、epoch 等字段。若是 date 类型则返回其年份;若是 timestamp 则返回其年份。date_part('year', ...) 是等效函数。

EXTRACT 返回 numeric(而非 int),是时间字段提取的通用函数。

SELECT EXTRACT(YEAR FROM DATE '2020-06-15'); -- 2020
#

41. INTERVAL '1 day' 的语义?

请说明 PostgreSQL 中 INTERVAL '1 day' 的语义?

  • 时间间隔
  • 与 timestamp 运算
  • 日与月

INTERVAL '1 day' 表示 1 天的时间间隔,与 timestamp 相加会得到精确的后一天同一时刻(如 '2020-01-01 12:00' + INTERVAL '1 day' = '2020-01-02 12:00')。它属于"日"单位,固定 24 小时语义(不涉及月长度的变化)。与 INTERVAL '1 month'(月长度可变)不同,日间隔是确定的。INTERVAL 是时间段,不是时间点。

'1 day' 是固定长度的日间隔,与可变的月间隔形成对比,用于时间点加减。

SELECT '2020-01-01 12:00:00'::timestamp + INTERVAL '1 day'; -- 2020-01-02 12:00:00
#

42. INTERVAL 与 Java Duration 的映射?

请说明 PostgreSQL 的 INTERVAL 与 Java Duration 的映射?

  • Duration 语义
  • 间隔粒度
  • 映射注意

PostgreSQL 的 INTERVAL 可表示天、时、分、秒等固定单位间隔,也可表示月、年等可变单位;Java 的 Duration 只表示固定长度的纳秒级时间量(如天、时、分、秒),不能表示月/年。因此仅当 INTERVAL 只含固定单位时才可无损映射到 Duration;含月/年的 INTERVAL 应映射到 Java 的 Period 或自定义类型。JDBC 通常将 INTERVAL 映射为字符串或 Period/Duration 组合。

映射核心是"固定单位 vs 可变单位",Duration 只覆盖固定单位部分。

#

43. TIMESTAMP 与 Java Instant 的映射?

请说明 PostgreSQL 的 TIMESTAMP 与 Java Instant 的映射?

  • Instant 是时间点
  • 与 timestamptz 匹配
  • timestamp 无时区

Java 的 Instant 表示绝对时间点(UTC),与 PostgreSQL 的 timestamptz(时间点)语义匹配,而非 timestamp(无时区墙钟时间)。因此读取 timestamptz 列可映射为 Instant;读取 timestamp 列应映射为 LocalDateTime(无时区)。若把 timestamp 列误映射为 Instant,会丢失时区语义或产生错误时刻。连接时区统一为 UTC 可简化映射。

Instant=时间点(timestamptz),LocalDateTime=墙钟时间(timestamp),映射须对齐语义。

#

44. date_trunc('day', ts) 的用法?

请说明 PostgreSQL 中 date_trunc('day', ts) 的用法?

  • 截断到字段
  • 返回类型
  • 分组统计

date_trunc('day', ts) 将时间戳截断到指定的最小单位(如 'day'、'month'、'hour'),返回该单位开始的时间戳,其余部分归零。例如 date_trunc('day', '2020-06-15 14:30:00') 返回 '2020-06-15 00:00:00'。常用于按天/月/小时分组统计,如每日订单量。date_trunc 返回类型与输入一致(timestamptz→timestamptz)。

date_trunc 是时间分组统计的核心工具,把连续时间规整到单位边界。

SELECT date_trunc('day', created_at), count(*) FROM orders GROUP BY 1;
#

45. timestamptz 的存储字节?

请说明 PostgreSQL 中 timestamptz 的存储字节数?

  • 8 字节
  • 微秒精度
  • 无时区存储

PostgreSQL 中 timestamptz 存储占用 8 字节,内部以 UTC 微秒精度表示绝对时刻,不存储时区偏移。timestamp 同样 8 字节。两者都不存储时区名,timestamptz 的时区只在输入输出时解释。8 字节足以覆盖数千年范围与微秒精度。

timestamptz 固定 8 字节、UTC 存储、微秒精度,是存储格式的基础事实。

#

46. MySQL 中表的默认排序规则?

请说明 MySQL 中表的默认排序规则如何确定?

  • 字符集与排序规则默认
  • 库级继承
  • 可覆盖

MySQL 中表的默认排序规则继承自数据库的默认字符集与排序规则,若未显式指定,则使用数据库默认。可在建表时用 CHARACTER SET 与 COLLATE 显式指定(如 CREATE TABLE t (...) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci)。MySQL 8.0 默认字符集为 utf8mb4,默认排序规则为 utf8mb4_0900_ai_ci。列级可覆盖表级。

默认排序规则沿"库→表→列"逐级继承,显式指定可覆盖,理解层级便于统一配置。

CREATE TABLE t (id INT) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;