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 查看新表内容。

常见坑

  1. 标识符大小写:DuckDB 对未加引号的标识符不区分大小写,但会按规则存入目录(catalog);Employees 与 employees 通常指同一对象。若用双引号创建 "MyTable",则后续必须用完全一致的大小写引用,否则报错。习惯上表名、列名统一用小写加下划线,少踩坑。
  2. 字符串引号:字符串字面量用单引号 'Engineering';双引号 "..." 在 DuckDB 里表示标识符(列名、表名),不是字符串。从 MySQL 习惯双引号字符串迁移时尤其容易写错。
  3. GROUP BY 与 SELECT 列不一致:SELECT 里写了未聚合、又不在 GROUP BY 中的列,会报错。先想清楚「按什么分组、算什么指标」,再写 SELECT 列表。
  4. 脚本重复执行:裸 CREATE TABLE 第二次运行会失败;教学脚本里更稳妥的是 CREATE OR REPLACE TABLE 或先 DROP TABLE IF EXISTS(生产环境需评估是否允许丢表)。
  5. 内存库与持久化:在内存库里建的 employees 关闭 CLI 即消失;需要保留示例数据请用 duckdb my.db 或在 CLI 内 .open my.db(见 简介与安装)。

延伸阅读


上一篇:简介与安装 · 下一篇:SQL 进阶