Kli*_*Max 16 c# linq sql-server-2008-r2
我有一个包含id的列表:
var myList = new List<int>();
Run Code Online (Sandbox Code Playgroud)
我想从db中选择来自myList的id的所有对象:
var objList= myContext.MyObjects.Where(t => myList.Contains(t.Id)).ToList();
Run Code Online (Sandbox Code Playgroud)
但是当myList.Count > 8000我收到错误时:
查询处理器耗尽了内部资源,无法生成查询计划.这是一种罕见的事件,仅适用于引用大量表或分区的极其复杂的查询或查询.请简化查询.如果您认为错误地收到了此消息,请与客户支持服务联系以获取更多信息.
我认为这是因为我使用过Contains().我可以使用什么而不是包含?
Mar*_*age 14
您可以通过添加AsEnumerable()"隐藏" Where实体框架中的子句来在客户端执行查询:
var objList = myContext
.MyObjects
.AsEnumerable()
.Where(t => myList.Contains(t.Id))
.ToList();
Run Code Online (Sandbox Code Playgroud)
要提高性能,您可以使用以下命令替换列表HashSet:
var myHashSet = new HashSet<int>(myList);
Run Code Online (Sandbox Code Playgroud)
然后相应地修改谓词Where:
.Where(t => myHashSet.Contains(t.Id))
Run Code Online (Sandbox Code Playgroud)
就实施时间而言,这是"简单"的解决方案.但是,由于查询正在运行客户端,因此可能会导致性能不佳,因为MyObjects在筛选之前会将所有行都提取到客户端.
您收到错误的原因是因为Entity Framework将您的查询转换为以下内容:
SELECT ...
FROM ...
WHERE column IN (ID1, ID2, ... , ID8000)
Run Code Online (Sandbox Code Playgroud)
因此,基本上,列表中的所有8000 ID都包含在生成的SQL中,超出了SQL Server可以处理的限制.
生成此SQL的实体框架"查找" ICollection<T>是由两者实现的List<T>,HashSet<T>因此如果您尝试在服务器端保留查询,则无法通过使用来提高性能HashSet<T>.然而,在客户端的故事,不同的是这里Contains是O(1)为HashSet<T>与O(N)对List<T>.
如果你不想这样做我建议你使用表值参数和存储过程.
在您的数据库中,使用TSQL,
CREATE TYPE [dbo].[IdSet] AS TABLE
(
[Id] INT
);
GO
CREATE PROCEDURE [dbo].[Get<table>]
@ids [dbo].[IdSet] READONLY
AS
SET NOCOUNT ON;
SELECT
<Column List>
FROM
[dbo].[<table>] [T]
WHERE
[T].[Id] IN (SELECT [Id] FROM @ids);
RETURN 0;
GO
Run Code Online (Sandbox Code Playgroud)
然后,在C#中
var ids = new DataTable()
ids.Columns.Add("Id", typeof(int));
foreach (var id in myList)
{
ids.Rows.Add(id);
}
var objList = myContext.SqlQuery<<entity>>(
"[dbo].[Get<table>] @ids",
new SqlParameter("@ids", SqDbType.Structured)
{
Value = ids,
TypeName = "[dbo].[IdSet]"
}));
Run Code Online (Sandbox Code Playgroud)
您可以创建一个临时数据库表,该表使用该临时List 表示myList和重构您的查询JOIN.
出错的原因是生成的实际查询包含的所有元素myList.
基本上,DB(查询处理器)需要查看两个列表才能进行过滤.如果第二个列表太大而不适合查询,则必须另外提供(例如作为临时表)
您可以将列表拆分为多个子列表,并运行单独的查询:
int start = 0;
int count = 0;
const int chunk_size = 1000;
do {
count = Math.Min(chunk_size, myList.Count - start);
var tmpList = myList.GetRange(start, count);
// run query with tmpList
var objList= myContext.MyObjects.Where(t => tmpList.Contains(t.Id)).ToList();
// do something with results...
start += count;
} while (start < myList.Count);
Run Code Online (Sandbox Code Playgroud)
当然,你需要以适合自己的某种方式找出好的"块大小".根据表和列表的大小,在代码中加载整个表和过滤器可能更方便,如其他答案中所建议的那样.
| 归档时间: |
|
| 查看次数: |
2028 次 |
| 最近记录: |