使用实体数组进行批量连表查询
场景说明
在实际业务中,经常需要根据一组条件批量查询某张表。例如:有一批学生信息(姓名、年龄、班级),需要判断哪些在数据库中存在、哪些不存在。
传统做法是逐条查询或在 SQL 中用 IN 拼接,而 LINQ 提供了一种更优雅的方式——将条件封装为实体数组,直接与数据表进行连接查询。
表结构
CREATE TABLE [dbo].[Student](
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [nvarchar](50) NULL,
[age] [int] NULL,
[birthday] [datetime] NULL,
[nation] [nvarchar](20) NULL,
[classname] [nvarchar](20) NULL,
CONSTRAINT [PK_Student] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]Code language: CSS (css)
查询条件实体
public class SearchKeyInfo
{
public string name;
public int age;
public string classname;
}Code language: PHP (php)
完整示例代码
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
using (var db = new DataClasses1DataContext())
{
// 构造查询条件列表
var list = new List<SearchKeyInfo>();
list.Add(new SearchKeyInfo() { name = "张三丰", age = 12, classname = "五班" });
list.Add(new SearchKeyInfo() { name = "李四", age = 36, classname = "二班" });
list.Add(new SearchKeyInfo() { name = "赵六", age = 2, classname = "四班" });
// 方式一:内连接 —— 只返回能匹配到的记录
var q = from l in list
join s in db.Student
on new { l.name, l.age, l.classname }
equals new { s.name, s.age, s.classname }
select new { l, s };
var result = q.ToList();
// 方式二:调用 ToArray() 后再连接(效果相同,条件数组先加载到内存)
var q1 = from l in list.ToArray()
join s in db.Student
on new { l.name, l.age, l.classname }
equals new { s.name, s.age, s.classname }
select new { l, s };
var result1 = q1.ToList();
// 方式三:左连接 —— 判断哪些条件能查到、哪些查不到
var q3 = from l in list
join s in db.Student
on new { l.name, l.age, l.classname }
equals new { s.name, s.age, s.classname } into sl
from g in sl.DefaultIfEmpty()
select new { l, g, nofound = g == null };
var result3 = q3.ToList();
}
}
}
public class SearchKeyInfo
{
public string name;
public int age;
public string classname;
}
}Code language: JavaScript (javascript)
三种方式对比
| 方式 | 连接类型 | 行为 | 适用场景 |
|---|---|---|---|
方式一 q | 内连接 | 只返回数据库中存在匹配的条件记录 | 批量校验哪些条件有效 |
方式二 q1 | 内连接 + ToArray() | 先将条件列表加载到内存再连接,效果同上 | 条件数据量小、需提前物化 |
方式三 q3 | 左连接 | 所有条件都列出,匹配不到的 g 为 null,nofound 标记是否存在 | 批量校验哪些条件缺失 |
执行结果示意
假设数据库中只有”李四, 36, 二班”存在:
方式一 / 方式二结果(仅 1 条):
┌────────────┬──────────────────────────────┐
│ 条件 │ 匹配的学生记录 │
├────────────┼──────────────────────────────┤
│ 李四,36,二班│ { id:2, name:"李四", ... } │
└────────────┴──────────────────────────────┘
方式三结果(3 条,全部条件):
┌──────────────┬──────────────────┬─────────┐
│ 条件 │ 匹配记录 g │ nofound │
├──────────────┼──────────────────┼─────────┤
│ 张三丰,12,五班│ null │ True │
│ 李四,36,二班 │ { id:2, ... } │ False │
│ 赵六,2,四班 │ null │ True │
└──────────────┴──────────────────┴─────────┘Code language: PHP (php)
关键要点
匿名对象字段必须一致
复合键连接时,两边匿名对象的属性名、类型、顺序必须完全匹配:
// 正确
new { l.name, l.age, l.classname } equals new { s.name, s.age, s.classname }
// 错误:属性名不一致
new { l.name, l.age, l.classname } equals new { s.name, s.age, s.classname } // 如果 s 的属性名不同则编译失败Code language: JavaScript (javascript)
list vs list.ToArray()
| 写法 | 行为 |
|---|---|
from l in list | list 作为 IEnumerable,LINQ to SQL 会尝试将整个集合作为参数化查询发送 |
from l in list.ToArray() | 先调用 ToArray() 将集合物化到内存,后续连接操作在内存中完成 |
当条件集合较大时,建议先
ToArray()避免 LINQ to SQL 尝试将整个列表翻译为 SQL 参数数组导致参数过多异常。
左连接中 nofound 标记
select new { l, g, nofound = g == null }Code language: JavaScript (javascript)
通过 g == null 判断当前条件在数据库中是否无匹配,方便后续业务逻辑区分处理(如插入缺失数据)。
典型应用场景
| 场景 | 做法 |
|---|---|
| 批量查重 | 左连接,筛选 nofound == true 的记录进行插入 |
| 批量校验 | 内连接,只处理已存在的记录 |
| 数据同步 | 左连接,根据 nofound 决定 INSERT 或 UPDATE |
Previous: 多条件连接(复合键 Join)
Next: 一对多关系的数据插入(主从表事务)