7、SQLite高级查询:多表连接、子查询、UNION、DISTINCT、CASE WHEN
说实话,做Android开发这么多年,我见过太多人在数据库查询上栽跟头了。单表查询谁都会写,但一旦涉及到多张表、复杂条件,很多人就开始头皮发麻。今天咱们就把这些高级查询技巧一次性讲透。
我个人习惯把SQLite的高级查询分成两类:一类是「表与表之间的操作」,比如连接、合并;另一类是「行与行之间的处理」,比如去重、条件分支。咱们一个一个来。
7.1 多表连接:INNER JOIN 与 LEFT JOIN
先说说多表连接。你在实际项目里,数据很少只存在一张表里。比如用户表、订单表、商品表,它们之间通过外键关联。这时候就需要 JOIN 了。
INNER JOIN 说白了就是「取交集」——只返回两张表中匹配成功的行。我早期做电商项目时,查「已下单用户及其订单详情」就用这个。
-- 查询所有有订单的用户及其订单信息
SELECT
u.user_id,
u.user_name,
o.order_id,
o.amount,
o.order_date
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id;
你看,只有那些下过单的用户才会出现在结果里。没下过单的?对不起,不显示。
LEFT JOIN 则不同。它保留左表的所有行,右表没有匹配的,就用 NULL 填充。我在项目中遇到过这样一个场景:要展示所有用户列表,同时显示他们最近一次登录时间。没登录过的用户也要显示,只是登录时间为空。这时候 LEFT JOIN 就派上用场了。
-- 查询所有用户及其订单(包括没有订单的用户)
SELECT
u.user_id,
u.user_name,
o.order_id,
o.amount
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id;
7.2 子查询:查询里的查询
子查询,就是在一个 SQL 语句里再嵌套一个 SELECT。你想想看,有时候你需要先查出一个中间结果,再基于这个结果做二次过滤。子查询就是干这个的。
子查询可以出现在三个地方:WHERE 子句、FROM 子句、SELECT 子句。
WHERE 子查询 最常见。比如查「下单金额超过平均值的用户」:
SELECT user_id, user_name, total_amount
FROM users
WHERE total_amount > (
SELECT AVG(total_amount) FROM users
);
FROM 子查询 相当于把子查询结果当成一张临时表。我记得有一次做报表,需要先聚合再关联,用这个特别顺手:
SELECT t.user_id, t.order_count, u.user_name
FROM (
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
) t
INNER JOIN users u ON t.user_id = u.user_id;
SELECT 子查询 用得少一些,但某些场景很巧妙。比如查每个用户及其最新订单日期:
SELECT
user_id,
user_name,
(SELECT MAX(order_date) FROM orders WHERE orders.user_id = users.user_id) AS last_order_date
FROM users;
7.3 UNION 合并结果集
UNION 的作用很简单:把两个 SELECT 的结果上下拼在一起。但有个硬性要求——两个 SELECT 的列数必须相同,对应列的数据类型要兼容。
我举个例子。假设你要查「所有 VIP 用户和所有本月新注册用户」,这两个条件可能有重叠,但你想去重后展示:
SELECT user_id, user_name, 'VIP' AS source
FROM users
WHERE is_vip = 1
UNION
SELECT user_id, user_name, 'New' AS source
FROM users
WHERE reg_date >= '2025-01-01';
UNION 默认会去重。如果你不想去重,或者你知道两个结果集本来就没有重复,用 UNION ALL 会更快,因为它省去了排序去重的步骤。
7.4 DISTINCT 去重
DISTINCT 很简单,就是去掉结果集中完全重复的行。但很多人用错了地方。
-- 查询所有不重复的城市
SELECT DISTINCT city FROM users;
-- 多列去重:城市和性别都相同的才视为重复
SELECT DISTINCT city, gender FROM users;
嗯,这里要注意:DISTINCT 是对整个结果集的行做去重,不是只对某一列。你写 SELECT DISTINCT name, age,它去重的是 (name, age) 这个组合,而不是单独 name。
我曾经见过有人这么写:
SELECT DISTINCT name, age, address, phone FROM users;
结果发现数据根本去不掉——因为每个人的地址和电话都不一样。你想想看,这 DISTINCT 等于没起作用。正确的做法是只查你需要去重的列,或者用 GROUP BY 替代。
7.5 CASE WHEN 条件表达式
CASE WHEN 是 SQL 里的 if-else。它可以在查询结果里根据条件动态生成新的列值。我个人觉得这是 SQLite 里最被低估的功能之一。
基本语法有两种:
-- 简单 CASE 表达式
SELECT
user_name,
CASE level
WHEN 1 THEN '普通用户'
WHEN 2 THEN '银卡用户'
WHEN 3 THEN '金卡用户'
ELSE '未知'
END AS level_name
FROM users;
-- 搜索 CASE 表达式(更灵活)
SELECT
user_name,
total_amount,
CASE
WHEN total_amount > 10000 THEN 'VIP'
WHEN total_amount > 5000 THEN '高级'
WHEN total_amount > 1000 THEN '中级'
ELSE '初级'
END AS user_grade
FROM users;
我在项目中用 CASE WHEN 做过一个很有意思的功能:统计每个用户的订单状态分布。一条 SQL 就搞定了:
SELECT
user_id,
COUNT(*) AS total_orders,
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) AS completed_count,
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending_count,
SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) AS cancelled_count
FROM orders
GROUP BY user_id;
你看,本来需要写四条 SQL 或者用代码循环处理的事情,一条 CASE WHEN 就解决了。这就是 SQL 的魅力。
ORDER BY CASE WHEN ... THEN ... END 实现自定义排序。
7.6 综合实战:把这些技巧串起来
光讲理论没意思。我拿一个真实场景来串一下这些知识点。
假设我们有三张表:users(用户)、orders(订单)、products(商品)。需求是:查每个用户的「高价值订单」数量(金额 > 500 的订单),并按数量降序排列,只显示前 10 名。
SELECT
u.user_id,
u.user_name,
COUNT(DISTINCT o.order_id) AS high_value_orders,
CASE
WHEN COUNT(DISTINCT o.order_id) >= 5 THEN '超级买家'
WHEN COUNT(DISTINCT o.order_id) >= 3 THEN '活跃买家'
ELSE '普通买家'
END AS buyer_level
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id AND o.amount > 500
GROUP BY u.user_id
HAVING high_value_orders > 0
ORDER BY high_value_orders DESC
LIMIT 10;
这条 SQL 里用到了 LEFT JOIN(保留所有用户)、DISTINCT(去重订单)、CASE WHEN(分级)、GROUP BY + HAVING(分组过滤)、ORDER BY + LIMIT(排序取前 N)。你看,高级查询就是这么组合出来的。
o.amount > 500),结果发现有些用户明明有订单但金额不满足,他们的 high_value_orders 是 0,但 LEFT JOIN 仍然保留了这些用户。如果你只想统计有高价值订单的用户,应该把金额条件放到 WHERE 里,或者用 INNER JOIN。这个坑我踩过,分享给你。
好了,这一章的内容就到这里。SQLite 的高级查询其实没那么可怕,关键是理解每种操作的「语义」——它到底是在做什么样的数据变换。多写、多试、多踩坑,慢慢就熟练了。
公众号:蓝海资料掘金营,微信deep3321