前置知识: SQL

数据类型

6 minIntermediate2026/6/14

SQL数据类型体系:数值类型、字符串类型、日期时间类型、JSON类型、空间类型的语法、存储与最佳实践

1. 数据型概述

SQL 数据型定义了列、参数和表达式可以存储的数据种及其操作。合理选择数据型直接影响存储效率、查询性能和数据完整性。

1.1 数据型分

典型用途
数值INTEGER, DECIMAL, FLOAT, DOUBLE数值计算与存储
字符串CHAR, VARCHAR, TEXT, CLOB文本数据
日期时间DATE, TIME, TIMESTAMP, INTERVAL时间相关数据
布尔BOOLEAN逻辑真/假
JSON JSON, JSONB半结构化数据
空间GEOMETRY, POINT, LINESTRING, POLYGON地理空间数据
二进制BLOB, BINARY, VARBINARY二进制大对象

1.2 型选择原则

  • 最小化原则:选择能满足需求的最小数据型,减少存储和 I/O 开销
  • 精确性原则:货币等精确数值使用 DECIMAL,避免浮点精度丢失
  • 兼容性原则:考虑跨数据库的 SQL 标准兼容性

2. 数值

2.1 精确数值

字节范围说明
SMALLINT23276832767-32768 \sim 32767小整数
INTEGER / INT42312311-2^{31} \sim 2^{31}-1标准整数
BIGINT82632631-2^{63} \sim 2^{63}-1大整数
DECIMAL(p, s)变长取决于精度精确小数
NUMERIC(p, s)变长同 DECIMALSQL 标准别名

DECIMAL 精度说明

  • p(precision):总位数,不含小数点,范围 1~38(标准)或更大(实现相关)
  • s(scale):小数位数,0sp0 \le s \le p
-- 货币存储:精确到分
CREATE TABLE products (
    price DECIMAL(10, 2)  -- 最大 99999999.99
);

-- 科学测量:精确到微米
CREATE TABLE measurements (
    length DECIMAL(12, 6)  -- 最大 999999.999999
);

2.2 近似数值

字节精度范围
REAL / FLOAT46 位有效3.4×10383.4×1038-3.4 \times 10^{38} \sim 3.4 \times 10^{38}
DOUBLE PRECISION815 位有效1.7×103081.7×10308-1.7 \times 10^{308} \sim 1.7 \times 10^{308}

注意:浮点型遵循 IEEE 754 标准,存在精度丢失问题。比较浮点数时需使用容差:

-- 错误:浮点等值比较
SELECT * FROM sensors WHERE reading = 0.1;

-- 正确:使用容差范围
SELECT * FROM sensors WHERE ABS(reading - 0.1) < 1e-9;

2.3 自增

-- SQL 标准自增
CREATE TABLE users (
    id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    name VARCHAR(100)
);

-- 兼容写法(MySQL AUTO_INCREMENT, PostgreSQL SERIAL)
CREATE TABLE orders (
    id BIGINT AUTO_INCREMENT PRIMARY KEY  -- MySQL
);

3. 字符串

3.1 定长与变长字符串

最大长度说明
CHAR(n)n 字符定长,不足补空格
VARCHAR(n)n 字符变长,按实际存储
TEXT / CLOB无限制大文本,SQL 标准为 CLOB

CHAR vs VARCHAR 选择

  • 长度恒定的数据(如国家代码 CHAR(2)、MD5 CHAR(32))使用 CHAR
  • 长度变化的数据使用 VARCHAR,避免尾部空格浪费
CREATE TABLE customers (
    country_code CHAR(2),        -- 固定2位国家代码
    name VARCHAR(100),           -- 变长姓名
    bio TEXT                     -- 不限长度简介
);

3.2 国家字符集

说明
NCHAR(n)国家字符集定长字符串
NVARCHAR(n)国家字符集变长字符串
NCLOB国家字符集大文本
-- 存储多语言文本
CREATE TABLE i18n_messages (
    msg_key VARCHAR(50),
    content_zh NVARCHAR(500),   -- 中文
    content_ja NVARCHAR(500)    -- 日文
);

3.3 字符集与排序规则

-- 指定字符集和排序规则
CREATE TABLE articles (
    title VARCHAR(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
    content TEXT CHARACTER SET utf8mb4
);

-- 排序规则影响比较和排序
-- utf8mb4_general_ci: 不区分大小写,速度快
-- utf8mb4_unicode_ci: 不区分大小写,Unicode 正确排序
-- utf8mb4_bin: 区分大小写,二进制比较

4. 日期时间

4.1 标准日期时间

格式精度范围
DATEYYYY-MM-DD0001-01-01 ~ 9999-12-31
TIMEHH:MM:SS[.ffffff]微秒00:00:00 ~ 23:59:59.999999
TIMESTAMPYYYY-MM-DD HH:MM:SS[.fff]微秒0001 ~ 9999 年
TIME WITH TIME ZONE含时区偏移微秒
TIMESTAMP WITH TIME ZONE含时区偏移微秒
CREATE TABLE events (
    event_date DATE,
    event_time TIME(3),                    -- 精确到毫秒
    created_at TIMESTAMP WITH TIME ZONE    -- 含时区
);

-- 插入日期时间值
INSERT INTO events VALUES (
    DATE '2026-06-14',
    TIME '14:30:00.123',
    TIMESTAMP WITH TIME ZONE '2026-06-14 14:30:00+08:00'
);

4.2 INTERVAL

INTERVAL 表示时间跨度,用于日期时间运算:

-- 年-月间隔
INTERVAL '3-2' YEAR TO MONTH     -- 3年2个月

-- 日-时间隔
INTERVAL '5 12:30:00' DAY TO SECOND  -- 5天12小时30分

-- 日期运算
SELECT
    DATE '2026-06-14' + INTERVAL '30' DAY AS thirty_days_later,
    TIMESTAMP '2026-06-14 10:00:00' - INTERVAL '2' HOUR AS two_hours_ago;

4.3 时区处理最佳实践

-- 推荐:存储 UTC 时间,查询时转换时区
CREATE TABLE logs (
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- 查询时转换为本地时区
SELECT created_at AT TIME ZONE 'Asia/Shanghai' AS local_time
FROM logs;

5. JSON

5.1 JSON 与 JSONB

特性JSONJSONB
存储文本原样存储二进制解析后存储
写入速度快(无需解析)慢(需解析转换)
查询速度慢(每次需解析)快(已解析)
索引支持有限完整 GIN 索引支持
空格保留保留不保留
键顺序保留不保证
-- PostgreSQL JSONB
CREATE TABLE api_logs (
    id BIGSERIAL PRIMARY KEY,
    payload JSONB,
    created_at TIMESTAMP DEFAULT NOW()
);

-- 插入 JSON 数据
INSERT INTO api_logs (payload) VALUES (
    '{"user_id": 42, "action": "login", "meta": {"ip": "192.168.1.1"}}'
);

-- JSON 查询操作符
SELECT payload->>'user_id' AS user_id,          -- 文本提取
       payload->'meta'->>'ip' AS ip,            -- 嵌套提取
       jsonb_pretty(payload) AS formatted       -- 格式化输出
FROM api_logs
WHERE payload @> '{"action": "login"}'::jsonb;  -- 包含查询

5.2 JSON 路径查询(SQL:2016 标准)

-- SQL/JSON 路径表达式
SELECT *
FROM api_logs
WHERE payload ? '$.meta.ip ? (@ == "192.168.1.1")';

-- JSON_TABLE:将 JSON 转为关系表
SELECT jt.user_id, jt.action
FROM api_logs,
     JSON_TABLE(payload, '$' COLUMNS (
         user_id INTEGER PATH '$.user_id',
         action  VARCHAR(50) PATH '$.action'
     )) AS jt;

6. 空间数据

6.1 OGC 简单要素模型

SQL/MM 标准定义了空间数据型层次:

GEOMETRY
├── POINT
├── CURVE
│   ├── LINESTRING
│   └── CIRCULARSTRING
├── SURFACE
│   ├── POLYGON
│   └── CURVEPOLYGON
└── GEOMETRYCOLLECTION
    ├── MULTIPOINT
    ├── MULTILINESTRING
    └── MULTIPOLYGON

6.2 空间型使用

-- PostgreSQL + PostGIS
CREATE TABLE locations (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    geom GEOMETRY(Point, 4326)    -- SRID 4326 = WGS84
);

-- 插入空间数据
INSERT INTO locations (name, geom) VALUES (
    '天安门',
    ST_SetSRID(ST_MakePoint(116.3975, 39.9087), 4326)
);

-- 空间查询:3公里范围内的地点
SELECT name,
       ST_Distance(geom::geography,
                   ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326)::geography
       ) AS distance_meters
FROM locations
WHERE ST_DWithin(
    geom::geography,
    ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326)::geography,
    3000  -- 3公里
);

6.3 空间索引

-- 创建 GIST 空间索引
CREATE INDEX idx_locations_geom ON locations USING GIST (geom);

-- 空间操作符(使用索引)
SELECT * FROM locations
WHERE geom && ST_MakeEnvelope(116.3, 39.8, 116.5, 40.0, 4326);

7. 型转换

7.1 显式转换

-- CAST 函数(SQL 标准)
SELECT CAST('123' AS INTEGER);
SELECT CAST(price AS VARCHAR(20));

-- 类型转换简写(PostgreSQL)
SELECT '123'::INTEGER;
SELECT created_at::DATE;

-- 格式化转换
SELECT TO_CHAR(created_at, 'YYYY-MM-DD HH24:MI:SS') AS formatted;
SELECT TO_NUMBER('1,234.56', '9G999D99');

7.2 隐式转换规则

数据库在以下场景自动进行型转换:

  1. 赋值转换:插入值与列型不匹配时
  2. 比较转换:不同型比较时,通常向”更宽”型转换
  3. 运算转换:如 INTEGER + DECIMAL → DECIMAL
-- 隐式转换示例
SELECT * FROM users WHERE id = '42';     -- '42' → 42
SELECT '2026-06-14'::DATE + 1;           -- DATE + INTEGER → DATE

最佳实践:避免依赖隐式转换,显式使用 CAST 提高代码可读性和可移植性。