Dapper多表查询怎么写?QueryMultiple与性能优化实践
2026-10-01 1 0
在.NET项目中使用 Dapper 查询订单、用户、商品等关联数据时,经常会遇到多表查询需求。Dapper 提供了 Multi-Mapping 和 QueryMultiple 两种常见方案。
如果多个查询之间没有必要通过 JOIN 合并成一张结果集,可以使用 QueryMultiple 一次发送多条 SQL,再依次读取不同结果集。Dapper 官方文档也将它作为处理多个结果集的方式。
Dapper多表查询有哪些写法?
假设有 Users、Orders、OrderItems 三张表,需要一次获取用户信息、订单列表和订单明细。
var sql = @"
SELECT Id, Name FROM Users WHERE Id = @UserId;
SELECT Id, UserId, OrderNo, TotalAmount
FROM Orders
WHERE UserId = @UserId;
SELECT oi.Id, oi.OrderId, oi.ProductId, oi.Quantity
FROM OrderItems oi
INNER JOIN Orders o ON oi.OrderId = o.Id
WHERE o.UserId = @UserId;
";
using var connection = new SqlConnection(connectionString);
using var multi = connection.QueryMultiple(sql, new { UserId = userId });
var user = multi.Read<User>().SingleOrDefault();
var orders = multi.Read<Order>().ToList();
var items = multi.Read<OrderItem>().ToList();
这里执行一次数据库请求,返回三个结果集。Read<T>() 按 SQL 中 SELECT 出现的顺序读取数据。
这种方式特别适合一个页面需要同时加载多组互相独立的数据,例如用户详情页、订单详情页、后台统计页面等。
QueryMultiple和JOIN怎么选?
如果查询结果本身就是一张关联数据表,例如订单和用户一对一关联,可以使用 Dapper Multi-Mapping:
var sql = @"
SELECT
o.Id,
o.OrderNo,
o.TotalAmount,
u.Id,
u.Name
FROM Orders o
INNER JOIN Users u ON u.Id = o.UserId
WHERE o.Id = @Id";
var order = connection.Query<Order, User, Order>(
sql,
(order, user) =>
{
order.User = user;
return order;
},
new { Id = orderId },
splitOn: "Id"
).SingleOrDefault();
Multi-Mapping适合把 JOIN 查询结果映射成对象关系。QueryMultiple 更适合一次获取多组独立结果。两者的区别主要取决于数据关系和最终需要的数据结构。
QueryMultiple为什么适合性能优化?
假设页面需要查询用户、订单和统计信息。连续执行三个 Query,通常需要进行三次数据库往返。改成 QueryMultiple 后,可以把多条 SELECT 放进同一个 SQL 批次,一次提交给数据库,再读取多个结果集。减少网络往返次数是它比较明显的优势。
不过,QueryMultiple 并不会自动让复杂 SQL 变快。如果某个 SELECT 本身需要扫描大量数据,查询耗时依然主要取决于数据库执行计划、索引和返回数据量。
Dapper多表查询如何优化?
第一,避免 SELECT *。
SELECT Id, OrderNo, UserId, TotalAmount
FROM Orders
WHERE UserId = @UserId;
只查询业务真正需要的字段,可以减少数据库读取和网络传输。
第二,给查询条件和 JOIN 字段建立合适的索引。例如:
CREATE INDEX IX_Orders_UserId
ON Orders(UserId);
具体索引仍然应该结合 WHERE、JOIN、ORDER BY 和实际执行计划决定。
第三,控制结果集数量。QueryMultiple 一次返回多个结果集很方便,但如果每个结果集都包含几万甚至几十万行,内存压力依然会增加。
Dapper 默认会缓冲查询结果。如果数据量很大,可以考虑使用 buffered: false 进行流式读取,但此时数据库连接和 DataReader 会保持更长时间,应尽快完成数据处理。
QueryMultiple使用时要注意什么?
Read<T>() 的读取顺序必须和 SQL 中的结果集顺序一致。
var users = multi.Read<User>().ToList();
var orders = multi.Read<Order>().ToList();
var statistics = multi.Read<OrderStatistics>().Single();
如果顺序写错,类型映射就可能出现异常。
另外,多条 SQL 最好统一使用参数:
new { UserId = userId }
不要把用户输入直接拼接进 SQL。参数化查询既能降低 SQL 注入风险,也方便 Dapper 对参数进行处理。
总结
Dapper 多表查询可以根据场景选择 Multi-Mapping 或 QueryMultiple。需要把 JOIN 数据映射成对象关系时,可以使用 Multi-Mapping。一个请求需要获取用户、订单、统计等多组独立数据时,QueryMultiple 通常更合适。
性能优化不能只盯着 Dapper API。SQL 字段数量、索引、返回行数、数据库执行计划以及网络往返次数,都会影响最终响应时间。实际项目中可以结合 SQL 执行计划和接口耗时进行测试,再决定是否使用 QueryMultiple。