类型与函数
本节目标
读完本篇,你应能:
- 选用常见的标量类型 (scalar types):数值、文本、布尔、日期与时间
- 创建并查询 LIST、STRUCT、MAP 等嵌套类型,并用
UNNEST展开列表 - 使用字符串、日期时间与聚合方面的常用函数
- 用
CAST/TRY_CAST做显式类型转换,并理解失败时的差异 - 避开 NULL 三值逻辑、TIMESTAMP 与时区、嵌套列访问等常见误区
若还不熟悉 JOIN、窗口函数等,请先阅读 SQL 进阶。
概念:类型决定能算什么
DuckDB 的类型系统整体接近 PostgreSQL,而不是 SQLite:整数有 INTEGER / BIGINT / HUGEINT,浮点有 FLOAT / DOUBLE,还有 DECIMAL(p, s) 固定精度小数。列式引擎会在执行前推断或校验类型;写错类型(例如把字符串当日期做减法)往往在解析或运行阶段就会报错。
除标量外,DuckDB 还提供 LIST(变长同类型数组)、STRUCT(固定键名的嵌套行)、MAP(键值对,各行键可不同)等嵌套类型,便于在单表里存 JSON 式或半结构化数据,而不必先拆成多张规范化表。
下面示例建议在 CLI 中按块复制执行。
标量类型:数值、文本、布尔、日期时间
数值:日常计数用 INTEGER;金额、科学计数可用 DOUBLE 或 DECIMAL(18, 2) 控制小数位。
SELECT
42::INTEGER AS n,
3.14::DOUBLE AS approx,
1234.56::DECIMAL(10, 2) AS money;
:: 是 PostgreSQL 风格的类型后缀,等价于 CAST(expr AS type)。
文本:VARCHAR(变长字符串)最常用;字面量用单引号 'hello'。
布尔:BOOLEAN(别名 BOOL),字面量 true / false;与 NULL 组合时见下文「常见坑」。
日期与时间:
| 类型 | 含义 |
|---|---|
DATE | 日历日期(年-月-日) |
TIME | 一天中的时刻(不含时区) |
TIMESTAMP | 日期 + 时间(别名 DATETIME,默认不含时区语义) |
TIMESTAMP WITH TIME ZONE | 带时区的时间戳(别名 TIMESTAMPTZ) |
INTERVAL | 时间间隔(如 INTERVAL '7 days') |
SELECT
DATE '2026-10-12' AS d,
TIME '14:30:00' AS t,
TIMESTAMP '2026-10-12 14:30:00' AS ts,
TIMESTAMPTZ '2026-10-12 14:30:00+08:00' AS tstz;
从字符串解析日期时,可用 strptime / try_strptime(见下文日期函数)。
嵌套类型:LIST、STRUCT、MAP
LIST
列表元素类型必须一致;用方括号或 list_value 构造:
SELECT [1, 2, 3] AS ids, ['duck', 'goose', NULL] AS names;
从多行聚合成列表(每组一行一个列表):
CREATE OR REPLACE TABLE orders (
customer_id INTEGER,
item VARCHAR
);
INSERT INTO orders VALUES
(1, 'apple'),
(1, 'banana'),
(2, 'apple');
SELECT customer_id, list(item) AS items
FROM orders
GROUP BY customer_id;
展开列表:UNNEST(list_column) 把一行里的列表变成多行(与 PostgreSQL 的 unnest 类似):
SELECT customer_id, unnest(items) AS item
FROM (
SELECT customer_id, list(item) AS items
FROM orders
GROUP BY customer_id
);
列表下标从 1 开始:list[1] 取第一个元素。
STRUCT
每行的 struct 必须有相同的键名;用 struct_pack 或 {'key': expr, ...} 构造:
SELECT {'name': 'Alice', 'score': 98} AS profile;
访问字段:profile.name 或 struct_extract(profile, 'name')。
CREATE OR REPLACE TABLE events (
id INTEGER,
payload STRUCT(name VARCHAR, score INTEGER)
);
INSERT INTO events VALUES
(1, {'name': 'Alice', 'score': 98}),
(2, {'name': 'Bob', 'score': 87});
SELECT id, payload.name AS user_name, payload.score
FROM events
WHERE payload.score >= 90;
键名在 schema 中固定;某行缺键会报错或与 struct 布局不一致——与 MAP 的「每行键可不同」形成对比。
MAP
键、值各自单一类型;各行可以有不同的键集合。查不到键时返回 NULL(不抛错):
SELECT MAP {'east': 100, 'west': 120} AS sales_by_region;
SELECT sales_by_region['east'] AS east_sales,
sales_by_region['north'] AS north_sales -- 无此键 → NULL
FROM (SELECT MAP {'east': 100, 'west': 120} AS sales_by_region);
也可用两个列表构造:MAP(['k1', 'k2'], [10, 20])。
常用函数
字符串
SELECT
length('DuckDB') AS len,
upper('duckdb') AS u,
lower('DUCKDB') AS l,
concat('Hello', ' ', 'World') AS greeting,
replace('foo-bar', '-', '_') AS swapped,
split_part('a.b.c', '.', 2) AS middle;
LIKE / ILIKE 做模式匹配;需要正则时用 regexp_matches 等(见 Text Functions)。
日期与时间
SELECT
current_date AS today,
current_timestamp AS now_ts,
date_part('year', DATE '2026-10-12') AS yr,
date_add(DATE '2026-10-12', INTERVAL 7 DAY) AS next_week,
datediff('day', DATE '2026-01-01', DATE '2026-10-12') AS days_elapsed;
把非标准格式的字符串转成日期:
SELECT strptime('12/10/2026', '%d/%m/%Y')::DATE AS parsed;
格式不对时 strptime 会失败;探索脏数据时用 try_strptime,失败返回 NULL。
聚合
与 SQL 基础 中一致:count(*)、sum、avg、min、max;条件聚合可用 count(*) FILTER (WHERE ...)。对列表列整列聚合成一个列表用 list(expr)(见上节)。
SELECT
count(*) AS rows,
sum(amount) AS total,
avg(amount) AS avg_amt
FROM (VALUES (1, 10.0), (2, 20.0), (3, NULL)) t(id, amount);
sum / avg 等会忽略组内的 NULL 输入;若全为 NULL,结果多为 NULL。
CAST 与 TRY_CAST
显式转换的两种写法:
SELECT
CAST('42' AS INTEGER) AS a,
'42'::INTEGER AS b,
CAST('3.14' AS DOUBLE) AS c;
转换不可能时 CAST 报错,整句失败:
-- 会失败:'abc' 不能转成 INTEGER
-- SELECT CAST('abc' AS INTEGER);
TRY_CAST 在失败时返回 NULL,适合 ETL 或清洗阶段「能转则转、不能转留空」:
SELECT
try_cast('42' AS INTEGER) AS ok,
try_cast('abc' AS INTEGER) AS bad; -- NULL
日期、嵌套类型之间是否可 cast,以官方 Casting 为准;不确定时先在小样数据上试 TRY_CAST。
常见坑
- NULL 与三值逻辑:
NULL = NULL结果是 UNKNOWN(不是true),过滤行时当作不通过。判断缺失用IS NULL/IS NOT NULL;组合条件用AND/OR时要注意短路并不消除 NULL 传播。coalesce(a, b)返回第一个非 NULL;nullif(a, b)在相等时返回 NULL。 - 布尔与 NULL:
WHERE flag = true不会选出flag IS NULL的行;若 NULL 表示「未知」,需单独写OR flag IS NULL。 - 时区:
TIMESTAMP字面量不含时区偏移;TIMESTAMPTZ会按会话或系统时区解释。跨区报表时统一用TIMESTAMPTZ或在应用层固定 UTC,避免「同一时刻两种写法」对不齐。详见 Timestamp Types。 - UNNEST 与行数:对一个列表
UNNEST会把一行扩成多行;与JOIN混用时行数会乘积增长,先在小结果上验证行数。多个列表并行UNNEST时按下标对齐(长度不一致时行为需查文档并实测)。 - STRUCT 键 vs MAP 键:STRUCT 缺字段是 schema 问题;MAP 缺键返回 NULL。不要用 MAP 冒充「每行结构相同」的嵌套行,否则失去列式存储与类型检查的好处。
- 浮点与 DECIMAL:金额展示与对账优先
DECIMAL;DOUBLE比较时用round或转成 decimal,避免0.1 + 0.2类误差影响=判断。
延伸阅读
- Data Types Overview — 内置类型一览
- List · Struct · Map — 嵌套类型
- Text Functions · Date Functions · Aggregates
- Type Casting —
CAST/TRY_CAST - 系列索引:DuckDB 教程
- 下一篇:导入与导出 — CSV、Parquet、COPY 等