C# 将大整数列表连接到LINQ查询中

C# 将大整数列表连接到LINQ查询中,c#,asp.net,sql,linq,linq-to-sql,C#,Asp.net,Sql,Linq,Linq To Sql,我的LINQ查询返回以下错误: “传入的表格数据流(TDS)远程过程调用(RPC)协议流不正确。此RPC请求中提供的参数太多。最大值为2100” 我所需要的是统计所有的客户,他们的出生日期,我有他们的ID在列表中。 我的客户ID列表可能非常庞大(数百万条记录) 以下是查询: List<int> allClients = GetClientIDs(); int total = context.Clients.Where(x => allClients.Contains(x.Cl

我的LINQ查询返回以下错误: “传入的表格数据流(TDS)远程过程调用(RPC)协议流不正确。此RPC请求中提供的参数太多。最大值为2100”

我所需要的是统计所有的客户,他们的出生日期,我有他们的ID在列表中。 我的客户ID列表可能非常庞大(数百万条记录)

以下是查询:

List<int> allClients = GetClientIDs();

int total = context.Clients.Where(x => allClients.Contains(x.ClientID) && x.BirthDate != null).Count();
它会导致相同的错误

还试图以不同的方式制作它,它吞噬了所有的记忆:

List<int> allClients = GetClientIDs();

total = (from x in allClients.AsQueryable()
         join y in context.Clients
         on x equals y.ClientID
         where y.BirthDate != null
         select x).Count();
List allClients=getclientid();
总计=(从allClients.AsQueryable()中的x开始)
在context.Clients中加入y
关于x等于y.ClientID
其中y.BirthDate!=null
选择x.Count();

如上所述,您的查询可能被转换为:

select count(1)
from Clients
where ClientID = @id1 or ClientID = @id2 -- and so on up to the number of ids returned by GetClientIDs.
您将需要更改查询,以避免向其传递太多参数

要查看生成的SQL,可以设置
Clients.Log=Console.Out
,这将导致在执行时将其写入调试窗口

编辑:

分块的一种可能替代方法是将ID作为分隔字符串发送到服务器,并在数据库中创建一个UDF,该UDF可以将该字符串转换回列表

var clientIds = string.Jon(",", allClients);

var total = (from client in context.Clients
            join clientIds in context.udf_SplitString(clientIds)
                on client.ClientId equals clientIds.Id
            select client).Count();

Google上有很多UDF拆分字符串的例子。

正如格特·阿诺德(Gert Arnold)前面提到的,分块查询可以解决问题,但看起来很糟糕:

List<int> allClients = GetClientIDs();

int total = 0;

const int sqlLimit = 2000;

int iterations = allClients.Count() / sqlLimit;

for (int i = 0; i <= iterations; i++)
{
    List<int> tempList = allClients.Skip(i * sqlLimit).Take(sqlLimit).ToList();

    int thisTotal = context.Clients.Count(x => tempList.Contains(x.ClientID) && x.BirthDate != null);

    total = total + thisTotal;
}
List allClients=getclientid();
int-total=0;
const int sqlLimit=2000;
int iterations=allClients.Count()/sqlLimit;
for(int i=0;i templast.Contains(x.ClientID)和&x.BirthDate!=null);
总计=总计+此总计;
}

我们在工作中遇到了同样的问题。问题是
list.Contains()
在(val1,val2,…valN)语句中创建了一个
WHERE列,因此只能在其中输入多少值。事实上,我们最终做的是分批做,就像你做的一样

然而,我认为我可以为您提供一段更干净、更优雅的代码来完成这项工作。下面是一个扩展方法,它将添加到您通常使用的其他Linq方法中:

public static IEnumerable<IEnumerable<T>> BulkForEach<T>(this IEnumerable<T> list, int size = 1000)
{
    for (int index = 0; index < list.Count() / size + 1; index++)
    {
        IEnumerable<T> returnVal = list.Skip(index * size).Take(size).ToList();
        yield return returnVal;
    }
}

另一种替代方法可能是在查询时最快的方法是将CSV文件中的数字添加到数据库中的临时表中,然后执行联接查询


分块执行查询意味着您的客户机和数据库之间有很多往返。如果您感兴趣的ID列表是静态的或很少更改,我建议使用临时表。

如果您不介意将工作从数据库移动到应用程序服务器并拥有内存,请尝试此方法


int total=context.Clients.AsEnumerable()。其中(x=>allClients.Contains(x.ClientID)&&x.BirthDate!=null)。Count()

GetClientIDs
从哪里获取数据?如果它是从数据库中提取的,您可能希望将其合并到您的查询中,而不是将所有ID提取到一个单独的查询中使用
allClients.Contains(x.ClientID)
强制查询在本地工作。@HamletHakobyan否,
allClients
可以是包含linq to sql查询的
IQueryable
。如果是这样,它将作为SQL合并到
total
查询中。@GertArnold首先铸造
allClients.AsQueryable()
不能保证
allClients
IQueryable
。无论如何,即使
allClients
IQueryable
也不可能将整个LINQ查询转换为SQL查询。@HamletHakobyan可能。但朱哈尔的建议值得一试。Konstantin,如果不可能的话,我想你唯一的选择就是分块查询。
GetClientId
做什么?它从CSV文件中获取客户端ID。我想这意味着你的选择非常有限,分块请求是你最好的选择。实际上,我有一个问题。EF不是每个Insert语句都做一次往返吗?那么,通过EF向临时表中添加100000条记录,然后查询该数据是否比执行10000次选择慢呢?我认为EntityFramework不支持临时表的概念,所以我想通过执行原始SQL语句将数据放到服务器上,但回答您的问题,您是正确的,最后我检查了每个insert语句在EF中都是往返的,尽管有一些关于EF6的建议支持批处理。
public static IEnumerable<IEnumerable<T>> BulkForEach<T>(this IEnumerable<T> list, int size = 1000)
{
    for (int index = 0; index < list.Count() / size + 1; index++)
    {
        IEnumerable<T> returnVal = list.Skip(index * size).Take(size).ToList();
        yield return returnVal;
    }
}
foreach (var item in list.BulkForEach())
{
    // Do logic here. item is an IEnumerable<T> (in your case, int)
}  
public static void BulkForEach<T>(this IEnumerable<T> list, Action<IEnumerable<T>> action, int size = 1000)
{
    for (int index = 0; index < list.Count() / size + 1; index++)
    {
        IEnumerable<T> returnVal = list.Skip(index * size).Take(size).ToList();
        action.Invoke(returnVal);
    }
}
list.BulkForEach(p => { /* Do logic */ });