学习 ORMX 的 Union 查询功能,包括 UNION、UNION ALL 操作、多表联合查询、排序分页和聚合函数等高级用法。 关键字:Union 查询,UNION, UNION ALL, 联合查询,多表查询,集合操作
Union 查询
Union 概述
Union(联合查询)是 SQL 中用于合并多个 SELECT 查询结果集的 powerful 功能。ORMX 提供了完整的 Union 支持,让您能够轻松地将多个查询的结果组合在一起。
UNION 与 UNION ALL 的区别
- UNION:合并多个查询结果,并自动去除重复行
- UNION ALL:合并多个查询结果,保留所有行(包括重复行),性能更好
使用场景
Union 查询适用于以下场景:
- 合并来自同一表的不同条件的查询结果
- 合并来自多个结构相似的表的数据
- 将多个独立查询的结果组合成一个结果集
- 实现复杂的数据筛选和分类
// 示例:查询年龄大于 30 的用户和姓名为"Alice"的用户
var result = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age > 30))
.UnionAll<User>(qb => qb.Where(u => u.Username == "Alice"))
.GetList();
基本 Union 操作
创建 Union 查询
使用 UnionTable 方法创建第一个查询(作为主 SELECT),然后使用 Union 或 UnionAll 添加更多查询:
// 查询年龄大于 30 的用户
var result = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age > 30))
.UnionAll<User>(qb => qb.Where(u => u.Username == "Alice"))
.GetList();
Console.WriteLine(<div class="latex">$"结果数量:{result.Count}");
foreach (var user in result)
{
Console.WriteLine($</div>"用户:{user.Username}, 年龄:{user.Age}");
}
生成的 SQL:
SELECT * FROM "Users" WHERE "Age" > @p0
UNION ALL
SELECT * FROM "Users" WHERE "Username" = @p1
UNION(去重)
使用 Union 方法执行去重的联合查询:
// 查询年龄>=25 的用户,并去重
var result = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age >= 25))
.Union<User>(qb => qb.Where(u => u.Username == "Alice"))
.GetList();
// 即使 Alice 的年龄>=25,也只会返回一次
Console.WriteLine(<div class="latex">$"去重后的用户数量:{result.Count}");
生成的 SQL:
SELECT * FROM "Users" WHERE "Age" >= @p0
UNION
SELECT * FROM "Users" WHERE "Username" = @p1
多个 Union 操作
可以链式调用多个 Union 操作:
// 合并三个查询的结果
var result = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age < 30))
.UnionAll<User>(qb => qb.Where(u => u.Age > 40))
.UnionAll<User>(qb => qb.Where(u => u.Username == "Admin"))
.GetList();
Console.WriteLine($</div>"合并结果:{result.Count} 个用户");
生成的 SQL:
SELECT * FROM "Users" WHERE "Age" < @p0
UNION ALL
SELECT * FROM "Users" WHERE "Age" > @p1
UNION ALL
SELECT * FROM "Users" WHERE "Username" = @p2
Union 上下文查询
使用 Union 上下文
Union 查询可能涉及多张表,使用上下文方式可以统一访问所有参与 Union 的表:
// 使用上下文方式访问 Union 中的表
var result = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age > 30))
.UnionAll<User>(qb => qb.Where(u => u.Username == "Alice"))
// 使用上下文方式访问 Union 中的表
.Where(ctx => ctx.As<User>().Age > 25)
.GetList();
生成的 SQL:
SELECT * FROM (
SELECT * FROM "Users" WHERE "Age" > @p1
UNION ALL
SELECT * FROM "Users" WHERE "Username" = @p2
) AS union_result
WHERE "Age" > @p0
上下文的优势
使用上下文方式的优势:
- 统一访问:通过
ctx.As<T>()统一访问所有参与 Union 的表 - 外层过滤:可以对 Union 后的结果集进行再次过滤
- 灵活性:适用于复杂的 Union 查询场景
// 先 Union,然后在外层过滤
var result = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age > 30))
.UnionAll<User>(qb => qb.Where(u => u.Username == "Alice"))
.Where(ctx => ctx.As<User>().Age > 25) // 对外层结果过滤
.GetList();
Union 排序和分页
OrderBy 排序
使用 OrderBy 对 Union 结果进行排序:
// 按年龄升序排序
var result = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age == 25))
.UnionAll<User>(qb => qb.Where(u => u.Age == 35))
.OrderBy(ctx => ctx.As<User>().Age)
.GetList();
// 结果按年龄从小到大排列
foreach (var user in result)
{
Console.WriteLine(<div class="latex">$"用户:{user.Username}, 年龄:{user.Age}");
}
生成的 SQL:
SELECT * FROM (
SELECT * FROM "Users" WHERE "Age" = @p0
UNION ALL
SELECT * FROM "Users" WHERE "Age" = @p1
) AS union_result
ORDER BY "Age" ASC
OrderByDesc 降序
使用 OrderByDesc 对 Union 结果进行降序排序:
// 按年龄降序排序
var result = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age == 30))
.UnionAll<User>(qb => qb.Where(u => u.Age == 35))
.OrderByDesc(ctx => ctx.As<User>().Age)
.GetList();
// 结果按年龄从大到小排列
生成的 SQL:
SELECT * FROM (
SELECT * FROM "Users" WHERE "Age" = @p0
UNION ALL
SELECT * FROM "Users" WHERE "Age" = @p1
) AS union_result
ORDER BY "Age" DESC
Limit 分页
使用 Limit 对 Union 结果进行分页:
// 只返回第一条记录
var result = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age == 25))
.UnionAll<User>(qb => qb.Where(u => u.Age == 30))
.Limit(1)
.GetList();
Console.WriteLine($</div>"第一条记录:{result[0].Username}");
生成的 SQL:
SELECT * FROM (
SELECT * FROM "Users" WHERE "Age" = @p0
UNION ALL
SELECT * FROM "Users" WHERE "Age" = @p1
) AS union_result
LIMIT 1
组合使用
可以组合使用排序和分页:
// 按年龄排序,然后取前 5 条
var result = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age > 20))
.UnionAll<User>(qb => qb.Where(u => u.Username.Contains("a")))
.OrderBy(ctx => ctx.As<User>().Age)
.Limit(5)
.GetList();
Union 聚合函数
Count 计数
使用 Count 统计 Union 结果的行数:
// 统计 Union 结果的总行数
var totalCount = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age > 30))
.UnionAll<User>(qb => qb.Where(u => u.Username == "Alice"))
.Count();
Console.WriteLine(<div class="latex">$"总行数:{totalCount}");
Max/Min 最值
使用 Max 和 Min 获取 Union 结果的最大值和最小值:
// 获取 Union 结果中的最大年龄
var maxAge = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age > 20))
.UnionAll<User>(qb => qb.Where(u => u.Username.Contains("a")))
.Max(u => u.Age);
Console.WriteLine($</div>"最大年龄:{maxAge}");
// 获取 Union 结果中的最小年龄
var minAge = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age > 20))
.UnionAll<User>(qb => qb.Where(u => u.Username.Contains("a")))
.Min(u => u.Age);
Console.WriteLine(<div class="latex">$"最小年龄:{minAge}");
Sum/Avg 求和与平均
使用 Sum 和 Avg 计算 Union 结果的总和与平均值:
// 计算 Union 结果的年龄总和
var sumAge = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age > 20))
.UnionAll<User>(qb => qb.Where(u => u.Username.Contains("a")))
.Sum(u => u.Age);
Console.WriteLine($</div>"年龄总和:{sumAge}");
// 计算 Union 结果的平均年龄
var avgAge = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age > 20))
.UnionAll<User>(qb => qb.Where(u => u.Username.Contains("a")))
.Avg(u => u.Age);
Console.WriteLine(<div class="latex">$"平均年龄:{avgAge}");
多表 Union 查询
不同表的 Union
Union 不仅可以用于同一张表,还可以用于结构相似的不同表:
// 假设有 User 和 Admin 两张结构相似的表
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public int Age { get; set; }
}
public class Admin
{
public int Id { get; set; }
public string Username { get; set; }
public int Age { get; set; }
}
// 合并用户和管理员
var result = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age > 30))
.UnionAll<Admin>(qb => qb.Where(a => a.Age > 50))
.GetList();
foreach (var item in result)
{
Console.WriteLine($</div>"名称:{item.Username}, 年龄:{item.Age}");
}
注意: 参与 Union 的不同表必须有相似的列结构(列名和数据类型兼容)。
使用 DTO 接收结果
当 Union 涉及多张表时,建议使用 DTO 来接收结果:
public class PersonDto
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
// 使用 DTO 接收 Union 结果
var result = tableManager.UnionTable<User>(qb => qb.Select(u => new { u.Id, u.Username, u.Age }))
.UnionAll<Admin>(qb => qb.Select(a => new { a.Id, a.Username, a.Age }))
.GetList();
复杂条件查询
复杂 Where 条件
在 Union 子查询中使用复杂的 Where 条件:
// 每个 Union 子句都可以有独立的复杂条件
var result = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age > 20 && u.Age < 35))
.UnionAll<User>(qb => qb.Where(u => u.Username.Contains("a") || u.Username.Contains("e")))
.GetList();
生成的 SQL:
SELECT * FROM "Users" WHERE ("Age" > @p0) AND ("Age" < @p1)
UNION ALL
SELECT * FROM "Users" WHERE ("Username" LIKE @p2) OR ("Username" LIKE @p3)
外层 Where 过滤
结合上下文使用外层 Where 过滤:
// 内层 Union,外层过滤
var result = tableManager.UnionTable<User>(qb => qb.Where(u => u.Age > 30))
.UnionAll<User>(qb => qb.Where(u => u.Username == "Alice"))
.Where(ctx => ctx.As<User>().Age > 25) // 外层过滤
.OrderBy(ctx => ctx.As<User>().Age)
.GetList();
最佳实践
性能优化
- 优先使用 UNION ALL:如果不需要去重,使用
UNION ALL性能更好 - 减少 Union 子句数量:过多的 Union 子句会影响查询性能
- 合理使用索引:确保 Union 子句中的 Where 条件能使用索引
代码组织
- 使用有意义的变量名:让代码更易读
- 添加注释:说明每个 Union 子句的目的
- 使用 DTO:当 Union 涉及多表时,使用 DTO 接收结果
// 良好的代码组织示例
// 查询活跃用户和 VIP 用户
var activeOrVipUsers = tableManager.UnionTable<User>(
qb => qb.Where(u => u.IsActive == true) // 活跃用户
)
.UnionAll<User>(
qb => qb.Where(u => u.IsVip == true) // VIP 用户
)
.OrderBy(ctx => ctx.As<User>().Username)
.GetList();
注意事项
- 列数必须相同:参与 Union 的所有查询必须有相同数量的列
- 列类型必须兼容:对应位置的列数据类型必须兼容
- 列名以第一个查询为准:结果集的列名由第一个 SELECT 决定
- ORDER BY 位置:ORDER BY 只能放在最后一个 Union 子句之后
完整示例
用户分类查询
// 查询新用户、活跃用户和 VIP 用户
var result = tableManager.UnionTable<User>(
qb => qb.Where(u => u.CreatedAt > DateTime.Now.AddDays(-7)) // 新用户
)
.UnionAll<User>(
qb => qb.Where(u => u.LastLoginAt > DateTime.Now.AddDays(-1)) // 活跃用户
)
.UnionAll<User>(
qb => qb.Where(u => u.IsVip == true) // VIP 用户
)
.OrderBy(ctx => ctx.As<User>().Id)
.Limit(10)
.GetList();
Console.WriteLine(<div class="latex">$"查询结果:{result.Count} 个用户");
foreach (var user in result)
{
Console.WriteLine($</div>"用户:{user.Username}");
}
多地区数据合并
// 假设有不同地区的用户表
var allUsers = tableManager.UnionTable<User>(
qb => qb.Where(u => u.Region == "Beijing")
)
.UnionAll<User>(
qb => qb.Where(u => u.Region == "Shanghai")
)
.UnionAll<User>(
qb => qb.Where(u => u.Region == "Guangzhou")
)
.Count();
Console.WriteLine($"三个地区的总用户数:{allUsers}");
小结
本章介绍了 ORMX 的 Union 查询功能,包括:
- 基本操作:使用
UnionTable、Union和UnionAll创建联合查询 - 上下文查询:使用
ctx.As<T>()统一访问 Union 中的表 - 排序分页:使用
OrderBy、OrderByDesc和Limit控制结果 - 聚合函数:使用
Count、Max、Min、Sum、Avg统计结果 - 多表 Union:合并结构相似的不同表的数据
- 最佳实践:性能优化和代码组织建议
Union 是处理多查询合并的强大工具,合理使用可以简化复杂查询场景。