SQLite多表查询 #
一、JOIN概述 #
1.1 什么是JOIN #
sql
-- JOIN 用于根据相关列组合两个或多个表的行
-- 示例表结构
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER,
total REAL,
order_date DATE
);
CREATE TABLE order_items (
id INTEGER PRIMARY KEY,
order_id INTEGER,
product_name TEXT,
quantity INTEGER,
price REAL
);
INSERT INTO users VALUES (1, 'John', 'john@example.com');
INSERT INTO users VALUES (2, 'Jane', 'jane@example.com');
INSERT INTO users VALUES (3, 'Bob', 'bob@example.com');
INSERT INTO orders VALUES (1, 1, 100.00, '2024-01-01');
INSERT INTO orders VALUES (2, 1, 200.00, '2024-01-15');
INSERT INTO orders VALUES (3, 2, 150.00, '2024-01-20');
INSERT INTO order_items VALUES (1, 1, 'Product A', 2, 50.00);
INSERT INTO order_items VALUES (2, 2, 'Product B', 1, 200.00);
INSERT INTO order_items VALUES (3, 3, 'Product C', 3, 50.00);
1.2 JOIN类型 #
text
┌─────────────┬──────────────────────────────────────┐
│ JOIN类型 │ 说明 │
├─────────────┼──────────────────────────────────────┤
│ INNER JOIN │ 只返回匹配的行 │
│ LEFT JOIN │ 返回左表所有行,右表不匹配则为NULL │
│ CROSS JOIN │ 笛卡尔积 │
│ (RIGHT JOIN │ SQLite 3.39+ 支持) │
│ (FULL JOIN │ SQLite 3.39+ 支持) │
└─────────────┴──────────────────────────────────────┘
二、INNER JOIN #
2.1 基本语法 #
sql
-- INNER JOIN:只返回两个表中匹配的行
SELECT columns
FROM table1
INNER JOIN table2 ON table1.column = table2.column;
-- 示例
SELECT
users.name,
orders.id AS order_id,
orders.total
FROM users
INNER JOIN orders ON users.id = orders.user_id;
-- 输出:
-- name | order_id | total
-- -----|----------|-------
-- John | 1 | 100.00
-- John | 2 | 200.00
-- Jane | 3 | 150.00
-- Bob 没有订单,所以不出现
2.2 多表JOIN #
sql
-- 连接多个表
SELECT
users.name,
orders.id AS order_id,
order_items.product_name,
order_items.quantity
FROM users
INNER JOIN orders ON users.id = orders.user_id
INNER JOIN order_items ON orders.id = order_items.order_id;
-- 输出:
-- name | order_id | product_name | quantity
-- -----|----------|--------------|----------
-- John | 1 | Product A | 2
-- John | 2 | Product B | 1
-- Jane | 3 | Product C | 3
2.3 使用表别名 #
sql
-- 使用别名简化
SELECT
u.name,
o.id AS order_id,
oi.product_name
FROM users u
INNER JOIN orders o ON u.id = o.user_id
INNER JOIN order_items oi ON o.id = oi.order_id;
2.4 JOIN与WHERE #
sql
-- 在JOIN后使用WHERE过滤
SELECT
u.name,
o.total
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.total > 100;
-- 在JOIN条件中使用AND
SELECT
u.name,
o.total
FROM users u
INNER JOIN orders o ON u.id = o.user_id AND o.total > 100;
三、LEFT JOIN #
3.1 基本语法 #
sql
-- LEFT JOIN:返回左表所有行,右表不匹配则为NULL
SELECT columns
FROM table1
LEFT JOIN table2 ON table1.column = table2.column;
-- 示例
SELECT
u.name,
o.id AS order_id,
o.total
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- 输出:
-- name | order_id | total
-- -----|----------|-------
-- John | 1 | 100.00
-- John | 2 | 200.00
-- Jane | 3 | 150.00
-- Bob | NULL | NULL -- Bob没有订单,但仍然出现
3.2 查找未匹配的行 #
sql
-- 查找没有订单的用户
SELECT u.name
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL;
-- 输出:
-- name
-- -----
-- Bob
3.3 LEFT JOIN与COUNT #
sql
-- 统计每个用户的订单数(包括0订单的用户)
SELECT
u.name,
COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;
-- 输出:
-- name | order_count
-- -----|------------
-- John | 2
-- Jane | 1
-- Bob | 0
四、RIGHT JOIN和FULL JOIN #
4.1 RIGHT JOIN (SQLite 3.39+) #
sql
-- RIGHT JOIN:返回右表所有行,左表不匹配则为NULL
SELECT
u.name,
o.id AS order_id
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
-- 变通方法(使用LEFT JOIN)
SELECT
u.name,
o.id AS order_id
FROM orders o
LEFT JOIN users u ON u.id = o.user_id;
4.2 FULL JOIN (SQLite 3.39+) #
sql
-- FULL JOIN:返回两个表的所有行
SELECT
u.name,
o.id AS order_id
FROM users u
FULL JOIN orders o ON u.id = o.user_id;
-- 变通方法(使用UNION)
SELECT u.name, o.id AS order_id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
UNION ALL
SELECT u.name, o.id AS order_id
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id
WHERE u.id IS NULL;
五、CROSS JOIN #
5.1 笛卡尔积 #
sql
-- CROSS JOIN:返回两个表的笛卡尔积
SELECT
u.name,
p.product_name
FROM users u
CROSS JOIN products p;
-- 如果users有3行,products有5行
-- 结果有 3 * 5 = 15 行
5.2 使用场景 #
sql
-- 生成所有组合
-- 例如:生成所有用户和所有日期的组合
SELECT
u.name,
d.date
FROM users u
CROSS JOIN (
SELECT date('2024-01-01') AS date
UNION SELECT date('2024-01-02')
UNION SELECT date('2024-01-03')
) d;
六、自连接 #
6.1 基本自连接 #
sql
-- 自连接:表与自己连接
-- 示例:员工和经理
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
name TEXT,
manager_id INTEGER
);
INSERT INTO employees VALUES (1, 'CEO', NULL);
INSERT INTO employees VALUES (2, 'Manager A', 1);
INSERT INTO employees VALUES (3, 'Manager B', 1);
INSERT INTO employees VALUES (4, 'Employee 1', 2);
INSERT INTO employees VALUES (5, 'Employee 2', 3);
-- 查询员工和其经理
SELECT
e.name AS employee,
m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
-- 输出:
-- employee | manager
-- -----------|---------
-- CEO | NULL
-- Manager A | CEO
-- Manager B | CEO
-- Employee 1 | Manager A
-- Employee 2 | Manager B
6.2 查找重复记录 #
sql
-- 使用自连接查找重复
SELECT a.name, a.email
FROM users a
JOIN users b ON a.email = b.email AND a.id > b.id;
6.3 比较同行数据 #
sql
-- 比较同一表中的不同行
SELECT
a.name AS user1,
b.name AS user2,
a.salary - b.salary AS salary_diff
FROM employees a
JOIN employees b ON a.department_id = b.department_id AND a.id < b.id;
七、JOIN优化 #
7.1 使用索引 #
sql
-- 为JOIN列创建索引
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
-- 查询会使用索引
SELECT u.name, o.total
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
7.2 选择合适的JOIN类型 #
sql
-- 如果只需要匹配的行,使用INNER JOIN
SELECT u.name, o.total
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- 如果需要保留左表所有行,使用LEFT JOIN
SELECT u.name, o.total
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- 避免不必要的JOIN
-- 如果只需要用户信息,不要JOIN订单表
SELECT name FROM users WHERE id = 1;
7.3 减少JOIN的表数量 #
sql
-- 避免过多的JOIN
-- 不推荐:5个以上表的JOIN
-- 替代方案:使用子查询或CTE
WITH user_orders AS (
SELECT user_id, SUM(total) AS total
FROM orders
GROUP BY user_id
)
SELECT u.name, uo.total
FROM users u
LEFT JOIN user_orders uo ON u.id = uo.user_id;
八、复杂JOIN示例 #
8.1 多表统计 #
sql
-- 统计每个用户的订单数和总金额
SELECT
u.name,
COUNT(DISTINCT o.id) AS order_count,
COALESCE(SUM(o.total), 0) AS total_amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;
-- 输出:
-- name | order_count | total_amount
-- -----|-------------|-------------
-- John | 2 | 300.00
-- Jane | 1 | 150.00
-- Bob | 0 | 0.00
8.2 层级查询 #
sql
-- 查询员工层级
WITH RECURSIVE employee_hierarchy AS (
SELECT id, name, manager_id, 1 AS level
FROM employees
WHERE manager_id IS NULL
UNION ALL
SELECT e.id, e.name, e.manager_id, eh.level + 1
FROM employees e
JOIN employee_hierarchy eh ON e.manager_id = eh.id
)
SELECT
printf('%*s%s', (level - 1) * 2, '', name) AS hierarchy
FROM employee_hierarchy;
8.3 最近记录JOIN #
sql
-- 每个用户的最近订单
WITH latest_orders AS (
SELECT
user_id,
id,
total,
order_date,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_date DESC) AS rn
FROM orders
)
SELECT
u.name,
lo.total,
lo.order_date
FROM users u
LEFT JOIN latest_orders lo ON u.id = lo.user_id AND lo.rn = 1;
九、JOIN注意事项 #
9.1 NULL值处理 #
sql
-- JOIN条件中的NULL
-- NULL = NULL 不匹配
-- 使用COALESCE处理
SELECT *
FROM table1 t1
LEFT JOIN table2 t2 ON COALESCE(t1.col, '') = COALESCE(t2.col, '');
9.2 重复列名 #
sql
-- 避免列名冲突
-- 错误:两个表都有id列
SELECT id FROM users JOIN orders ON ...;
-- 正确:使用表别名
SELECT u.id AS user_id, o.id AS order_id
FROM users u
JOIN orders o ON u.id = o.user_id;
9.3 性能陷阱 #
sql
-- 避免在JOIN条件中使用函数
-- 不推荐
SELECT * FROM users u
JOIN orders o ON lower(u.email) = lower(o.email);
-- 推荐
SELECT * FROM users u
JOIN orders o ON u.email = o.email COLLATE NOCASE;
十、总结 #
JOIN类型对比 #
| JOIN类型 | 说明 | 返回行数 |
|---|---|---|
| INNER JOIN | 只返回匹配行 | 匹配的行 |
| LEFT JOIN | 左表所有行 | 左表行数 |
| RIGHT JOIN | 右表所有行 | 右表行数 |
| FULL JOIN | 两表所有行 | 两表行数之和减匹配 |
| CROSS JOIN | 笛卡尔积 | 两表行数乘积 |
最佳实践 #
- 为JOIN列创建索引
- 使用表别名提高可读性
- 选择合适的JOIN类型
- 避免过多的JOIN
- 注意NULL值处理
下一步,让我们学习索引!
最后更新:2026-03-27