SQLite高级查询 #

一、子查询 #

1.1 子查询概述 #

sql
-- 子查询:嵌套在其他查询中的查询
-- 可以出现在:
-- 1. SELECT 子句
-- 2. FROM 子句
-- 3. WHERE 子句
-- 4. HAVING 子句

-- 示例表
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT,
    department_id INTEGER
);

CREATE TABLE departments (
    id INTEGER PRIMARY KEY,
    name TEXT
);

CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    user_id INTEGER,
    total REAL,
    order_date DATE
);

1.2 标量子查询 #

sql
-- 返回单个值的子查询
SELECT 
    name,
    (SELECT COUNT(*) FROM orders WHERE orders.user_id = users.id) AS order_count
FROM users;

-- 在WHERE中使用
SELECT * FROM users
WHERE department_id = (SELECT id FROM departments WHERE name = 'Engineering');

-- 使用比较运算符
SELECT * FROM users
WHERE salary > (SELECT AVG(salary) FROM users);

1.3 列子查询 #

sql
-- 返回单列多行的子查询
-- 使用 IN
SELECT * FROM users
WHERE department_id IN (SELECT id FROM departments WHERE budget > 100000);

-- 使用 ANY/SOME
SELECT * FROM products
WHERE price > ANY (SELECT price FROM competitor_products);

-- 使用 ALL
SELECT * FROM products
WHERE price > ALL (SELECT price FROM competitor_products);

1.4 行子查询 #

sql
-- 返回单行多列的子查询
SELECT * FROM users
WHERE (department_id, salary) = (
    SELECT department_id, MAX(salary)
    FROM users
    GROUP BY department_id
    HAVING department_id = users.department_id
);

1.5 表子查询 #

sql
-- 返回多行多列的子查询(派生表)
SELECT u.name, o.total
FROM users u
JOIN (SELECT user_id, SUM(total) AS total FROM orders GROUP BY user_id) o
ON u.id = o.user_id;

-- 在FROM中使用
SELECT avg_total
FROM (
    SELECT user_id, AVG(total) AS avg_total
    FROM orders
    GROUP BY user_id
) AS user_averages
WHERE avg_total > 100;

1.6 EXISTS子查询 #

sql
-- EXISTS:检查子查询是否返回行
SELECT name FROM users
WHERE EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id);

-- NOT EXISTS:检查子查询是否不返回行
SELECT name FROM users
WHERE NOT EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id);

-- 相关子查询
SELECT u.name
FROM users u
WHERE EXISTS (
    SELECT 1 FROM orders o
    WHERE o.user_id = u.id
    AND o.total > 1000
);

二、CTE(公用表表达式) #

2.1 基本CTE #

sql
-- WITH 子句定义CTE
WITH user_orders AS (
    SELECT user_id, COUNT(*) AS order_count, SUM(total) AS total_spent
    FROM orders
    GROUP BY user_id
)
SELECT 
    u.name,
    uo.order_count,
    uo.total_spent
FROM users u
LEFT JOIN user_orders uo ON u.id = uo.user_id;

-- 多个CTE
WITH 
    active_users AS (
        SELECT * FROM users WHERE status = 1
    ),
    user_totals AS (
        SELECT user_id, SUM(total) AS total
        FROM orders
        GROUP BY user_id
    )
SELECT 
    au.name,
    ut.total
FROM active_users au
LEFT JOIN user_totals ut ON au.id = ut.user_id;

2.2 递归CTE #

sql
-- 递归CTE用于处理层级数据
CREATE TABLE categories (
    id INTEGER PRIMARY KEY,
    name TEXT,
    parent_id INTEGER
);

INSERT INTO categories VALUES
    (1, 'Electronics', NULL),
    (2, 'Computers', 1),
    (3, 'Phones', 1),
    (4, 'Laptops', 2),
    (5, 'Desktops', 2);

-- 递归查询层级结构
WITH RECURSIVE category_tree AS (
    -- 基础查询:顶级分类
    SELECT id, name, parent_id, 0 AS level, name AS path
    FROM categories
    WHERE parent_id IS NULL
    
    UNION ALL
    
    -- 递归查询:子分类
    SELECT 
        c.id, 
        c.name, 
        c.parent_id, 
        ct.level + 1,
        ct.path || ' > ' || c.name
    FROM categories c
    JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree ORDER BY path;

-- 输出:
-- id | name       | parent_id | level | path
-- ---|------------|-----------|-------|------------------------
-- 1  | Electronics| NULL      | 0     | Electronics
-- 2  | Computers  | 1         | 1     | Electronics > Computers
-- 4  | Laptops    | 2         | 2     | Electronics > Computers > Laptops
-- 5  | Desktops   | 2         | 2     | Electronics > Computers > Desktops
-- 3  | Phones     | 1         | 1     | Electronics > Phones

2.3 递归CTE示例 #

sql
-- 生成数字序列
WITH RECURSIVE numbers AS (
    SELECT 1 AS n
    UNION ALL
    SELECT n + 1 FROM numbers WHERE n < 10
)
SELECT * FROM numbers;

-- 输出:1 到 10

-- 计算斐波那契数列
WITH RECURSIVE fibonacci AS (
    SELECT 0 AS n, 0 AS value, 1 AS next_value
    UNION ALL
    SELECT n + 1, next_value, value + next_value
    FROM fibonacci
    WHERE n < 10
)
SELECT n, value FROM fibonacci;

-- 组织结构图
WITH RECURSIVE org_chart AS (
    SELECT id, name, manager_id, 0 AS level
    FROM employees
    WHERE manager_id IS NULL
    
    UNION ALL
    
    SELECT e.id, e.name, e.manager_id, oc.level + 1
    FROM employees e
    JOIN org_chart oc ON e.manager_id = oc.id
)
SELECT 
    printf('%*s%s', level * 2, '', name) AS org_structure
FROM org_chart;

三、窗口函数 #

3.1 窗口函数概述 #

sql
-- 窗口函数:对一组行进行计算,但不减少行数
-- SQLite 3.25.0+ 支持

-- 基本语法
function_name() OVER (
    PARTITION BY column
    ORDER BY column
    frame_clause
)

3.2 ROW_NUMBER #

sql
-- 行号
SELECT 
    name,
    age,
    ROW_NUMBER() OVER (ORDER BY age DESC) AS row_num
FROM users;

-- 分组内行号
SELECT 
    name,
    department_id,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_rank
FROM employees;

3.3 RANK和DENSE_RANK #

sql
-- RANK:排名,有并列跳过
SELECT 
    name,
    score,
    RANK() OVER (ORDER BY score DESC) AS rank
FROM students;

-- 输出:
-- name  | score | rank
-- ------|-------|-----
-- Alice | 100   | 1
-- Bob   | 95    | 2
-- Carol | 95    | 2
-- Dave  | 90    | 4    -- 跳过3

-- DENSE_RANK:排名,不跳过
SELECT 
    name,
    score,
    DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank
FROM students;

-- 输出:
-- name  | score | dense_rank
-- ------|-------|-----------
-- Alice | 100   | 1
-- Bob   | 95    | 2
-- Carol | 95    | 2
-- Dave  | 90    | 3    -- 不跳过

3.4 聚合窗口函数 #

sql
-- 累计求和
SELECT 
    order_date,
    amount,
    SUM(amount) OVER (ORDER BY order_date) AS running_total
FROM daily_sales;

-- 移动平均
SELECT 
    order_date,
    amount,
    AVG(amount) OVER (
        ORDER BY order_date
        ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
    ) AS moving_avg
FROM daily_sales;

-- 分组聚合
SELECT 
    name,
    department_id,
    salary,
    AVG(salary) OVER (PARTITION BY department_id) AS dept_avg_salary
FROM employees;

3.5 LAG和LEAD #

sql
-- LAG:获取前一行
SELECT 
    order_date,
    amount,
    LAG(amount) OVER (ORDER BY order_date) AS prev_amount,
    amount - LAG(amount) OVER (ORDER BY order_date) AS diff
FROM daily_sales;

-- LAG带偏移量
SELECT 
    order_date,
    amount,
    LAG(amount, 7) OVER (ORDER BY order_date) AS amount_7_days_ago
FROM daily_sales;

-- LEAD:获取后一行
SELECT 
    order_date,
    amount,
    LEAD(amount) OVER (ORDER BY order_date) AS next_amount
FROM daily_sales;

3.6 FIRST_VALUE和LAST_VALUE #

sql
-- FIRST_VALUE:窗口内第一个值
SELECT 
    name,
    department_id,
    salary,
    FIRST_VALUE(salary) OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_max_salary
FROM employees;

-- LAST_VALUE:窗口内最后一个值
SELECT 
    name,
    department_id,
    salary,
    LAST_VALUE(salary) OVER (
        PARTITION BY department_id 
        ORDER BY salary DESC
        ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
    ) AS dept_min_salary
FROM employees;

3.7 NTILE #

sql
-- NTILE:将行分成N组
SELECT 
    name,
    score,
    NTILE(4) OVER (ORDER BY score DESC) AS quartile
FROM students;

-- 输出:
-- name  | score | quartile
-- ------|-------|----------
-- Alice | 100   | 1
-- Bob   | 95    | 1
-- Carol | 90    | 2
-- Dave  | 85    | 2
-- Eve   | 80    | 3
-- Frank | 75    | 3
-- Grace | 70    | 4
-- Henry | 65    | 4

四、CASE表达式 #

4.1 简单CASE #

sql
-- 简单CASE表达式
SELECT 
    name,
    status,
    CASE status
        WHEN 1 THEN 'Active'
        WHEN 0 THEN 'Inactive'
        ELSE 'Unknown'
    END AS status_name
FROM users;

4.2 搜索CASE #

sql
-- 搜索CASE表达式
SELECT 
    name,
    score,
    CASE 
        WHEN score >= 90 THEN 'A'
        WHEN score >= 80 THEN 'B'
        WHEN score >= 70 THEN 'C'
        WHEN score >= 60 THEN 'D'
        ELSE 'F'
    END AS grade
FROM students;

4.3 CASE在聚合中 #

sql
-- 条件计数
SELECT 
    COUNT(*) AS total,
    SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS active_count,
    SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS inactive_count
FROM users;

-- 条件求和
SELECT 
    department_id,
    SUM(CASE WHEN gender = 'M' THEN salary ELSE 0 END) AS male_salary,
    SUM(CASE WHEN gender = 'F' THEN salary ELSE 0 END) AS female_salary
FROM employees
GROUP BY department_id;

五、复杂查询示例 #

5.1 分组排名 #

sql
-- 每个部门薪资前三名
WITH ranked_employees AS (
    SELECT 
        name,
        department_id,
        salary,
        RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_rank
    FROM employees
)
SELECT * FROM ranked_employees WHERE dept_rank <= 3;

5.2 同比环比计算 #

sql
-- 月度销售同比环比
WITH monthly_sales AS (
    SELECT 
        strftime('%Y-%m', order_date) AS month,
        SUM(total) AS total_sales
    FROM orders
    GROUP BY month
)
SELECT 
    month,
    total_sales,
    LAG(total_sales, 1) OVER (ORDER BY month) AS prev_month,
    LAG(total_sales, 12) OVER (ORDER BY month) AS same_month_last_year,
    ROUND((total_sales - LAG(total_sales, 1) OVER (ORDER BY month)) * 100.0 / 
          LAG(total_sales, 1) OVER (ORDER BY month), 2) AS mom_growth
FROM monthly_sales;

5.3 连续问题 #

sql
-- 找出连续登录超过3天的用户
WITH login_dates AS (
    SELECT DISTINCT 
        user_id,
        login_date,
        login_date - ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) AS grp
    FROM user_logins
),
consecutive_groups AS (
    SELECT 
        user_id,
        grp,
        COUNT(*) AS consecutive_days
    FROM login_dates
    GROUP BY user_id, grp
)
SELECT DISTINCT user_id
FROM consecutive_groups
WHERE consecutive_days >= 3;

六、总结 #

高级查询技术 #

技术 说明 示例
子查询 嵌套查询 WHERE id IN (SELECT …)
CTE 公用表表达式 WITH cte AS (…)
递归CTE 处理层级数据 WITH RECURSIVE
窗口函数 行间计算 ROW_NUMBER() OVER()
CASE 条件表达式 CASE WHEN … THEN … END

最佳实践 #

  1. 复杂查询使用CTE提高可读性
  2. 窗口函数避免自连接
  3. 递归CTE注意终止条件
  4. 合理使用索引优化子查询

下一步,让我们学习多表查询!

最后更新:2026-03-27