SQL 基础
本节目标
读完本篇,你应能:
- 用
CREATE TABLE定义表结构,并用INSERT INTO写入行 - 编写
SELECT查询,配合WHERE过滤、ORDER BY排序、LIMIT限制行数 - 使用
GROUP BY与聚合函数做汇总统计 - 识别 DuckDB Friendly SQL 中两种常用写法:
FROM在前(等价于SELECT *)与CREATE TABLE ... AS SELECT(CTAS) - 避开标识符大小写与字符串引号等常见方言差异
若尚未安装 CLI 或连接内存库/文件库,请先阅读 简介与安装。
概念:DuckDB 里的 SQL 长什么样
DuckDB 的 SQL 方言 (dialect) 整体接近 PostgreSQL,而不是 SQLite:类型系统、函数与不少语法与 PG 更一致。你在 CLI 或 Python 里执行的语句,会先被解析成逻辑计划,再在列式引擎上执行——本篇只关心声明式查询怎么写,不涉及执行计划。
一条典型查询的执行顺序在逻辑上是:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT。写 SQL 时顺序可以不同,但理解这个顺序有助于读懂聚合与过滤为何这样组合。
下面用一张虚构的 employees(员工) 表贯穿示例。建议在 duckdb 或 duckdb my.db 的 D 提示符下按块复制执行(每块以 ; 结束)。
建表与插入
先建表并插入几行数据:
CREATE OR REPLACE TABLE employees (
id INTEGER PRIMARY KEY,
name VARCHAR,
department VARCHAR,
salary DOUBLE
);
INSERT INTO employees VALUES
(1, 'Alice', 'Engineering', 120000),
(2, 'Bob', 'Engineering', 95000),
(3, 'Carol', 'Sales', 88000),
(4, 'Dave', 'Sales', 91000),
(5, 'Eve', 'Engineering', 105000);
CREATE OR REPLACE TABLE 在脚本里很常见:重复运行不会因表已存在而失败(官方 Friendly SQL 也推荐这种写法)。INSERT INTO ... VALUES 按列顺序填值;列很多时可用 INSERT INTO ... BY NAME(按列名对齐),进阶用法见 INSERT 文档。
查询、过滤、排序与 LIMIT
-- 选出工程部、薪资不低于 10 万的人,按薪资降序,只看前 2 行
SELECT id, name, department, salary
FROM employees
WHERE department = 'Engineering'
AND salary >= 100000
ORDER BY salary DESC
LIMIT 2;
WHERE 使用单引号包裹字符串字面量(见下文「常见坑」)。ORDER BY 默认升序,加 DESC 为降序。LIMIT n 只返回结果集的前 n 行;DuckDB 还支持 LIMIT 10% 这类按结果集比例截断的 Friendly SQL 写法(详见 LIMIT)。
聚合与 GROUP BY
-- 各部门人数与平均薪资
SELECT
department,
count(*) AS headcount,
round(avg(salary), 2) AS avg_salary
FROM employees
GROUP BY department
ORDER BY avg_salary DESC;
GROUP BY 把行按 department 分成多组;count(*)、avg(salary) 等聚合函数 (aggregate functions) 对每组各算一个值。SELECT 里出现的非聚合列必须出现在 GROUP BY 中(或使用聚合表达式)。DuckDB 还提供 GROUP BY ALL:根据 SELECT 中的非聚合列自动推断分组键,适合探索性查询(GROUP BY)。
Friendly SQL 入门
DuckDB 把一批让 SQL 更短、更易读的语法统称为 Friendly SQL。本节只练两个与基础查询最相关的点;完整清单见 Friendly SQL。
1. FROM 在前:省略 SELECT *
只查全表时,可以写成:
FROM employees;
这等价于 SELECT * FROM employees;。需要投影或过滤时仍写完整 SELECT ... FROM ... WHERE ...。
2. CTAS:用查询结果直接建表
CREATE OR REPLACE TABLE eng_high AS
SELECT name, salary
FROM employees
WHERE department = 'Engineering'
AND salary >= 100000;
FROM eng_high ORDER BY salary DESC;
CREATE TABLE ... AS SELECT(CTAS)根据查询结果推断列类型,省去手工写 DDL。最后一行再次演示 FROM-first 查看新表内容。
常见坑
- 标识符大小写:DuckDB 对未加引号的标识符不区分大小写,但会按规则存入目录(catalog);
Employees与employees通常指同一对象。若用双引号创建"MyTable",则后续必须用完全一致的大小写引用,否则报错。习惯上表名、列名统一用小写加下划线,少踩坑。 - 字符串引号:字符串字面量用单引号
'Engineering';双引号"..."在 DuckDB 里表示标识符(列名、表名),不是字符串。从 MySQL 习惯双引号字符串迁移时尤其容易写错。 - GROUP BY 与 SELECT 列不一致:SELECT 里写了未聚合、又不在
GROUP BY中的列,会报错。先想清楚「按什么分组、算什么指标」,再写 SELECT 列表。 - 脚本重复执行:裸
CREATE TABLE第二次运行会失败;教学脚本里更稳妥的是CREATE OR REPLACE TABLE或先DROP TABLE IF EXISTS(生产环境需评估是否允许丢表)。 - 内存库与持久化:在内存库里建的
employees关闭 CLI 即消失;需要保留示例数据请用duckdb my.db或在 CLI 内.open my.db(见 简介与安装)。
延伸阅读
- SELECT 语法 — 投影、表达式与别名
- CREATE TABLE — 约束、默认值与 CTAS
- Friendly SQL —
GROUP BY ALL、SELECT * EXCLUDE等 - Keywords and Identifiers — 大小写与引号规则
- 系列索引:DuckDB 教程
- 下一篇:SQL 进阶 — JOIN、CTE、窗口函数等