我有一个看似简单的 linq-to-sql 查询,它可以搜索多个列中的某些数据;像这样的:
List<long> TheTableIDs = list of IDs (sometimes more than 2100)
var QueryOutput = (from x in TheDataContext.SomeTable
where TheTableIDs.Contains(x.ID) &&
x.Col1.Contains(SomeString) ||
x.Col2.Contains(SomeString))
select x.ID).ToList();
我得到的错误是这样的:
附加信息:传入请求参数过多。 服务器最多支持2100个参数。减少数量 参数并重新发送请求。
解决这个问题的最佳方法是什么?
我环顾四周,找到了一个解决方案,如下所示:
var QueryOutput = TheDataContext.SomeTable.AsEnumerable()
.Join(TheTableIDs, x => x.LeadID, ci => ci, (x, ci) => x)
.Where(x => SomeString.Contains(x.Col1) == true ||
SomeString.Contains(x.Col2) == true)
.Select(x => x.ID).ToList();
此编译不会引发任何异常,但似乎忽略了带有
Where
的
SomeString.Contains
子句
使该查询有效的解决方案是什么?
简单 - 只要 TheTAbleID 包含的 ID 少于 2100 个 - 这样做是不合法的。
将表切成 2000 个块,然后单独查询每个块,可能在多个线程中。
SQL 不支持在
in
语句中使用超过 2100 个值,但您可以在具有超过 2100 行的表中使用,这样您就可以将数据插入表中并更改查询以通过选择来检查 in
那张桌子
例如
Create TempIDs (bigint ID, uniqueidentifier guid)
guid 列用于防止混合不同的用户数据
在你的代码中
Guid myKey = Guid.New();
List<long> TheTableIDs = list of IDs (sometimes more than 2100)
TheDataContext.TempIDs.InsertAllOnSubmit(TheTableIDs.select(i => new TempIDs{ID = i, Guid = mykey});
TheDataContext.SubmitChanges();
var QueryOutput = (from x in TheDataContext.SomeTable
where TheDataContext.TempIDs.Contains(x.ID) &&
x.Col1.Contains(SomeString) ||
x.Col2.Contains(SomeString))
select x.ID).ToList();
如果您可以从数据库中检索 ids,您可以在 sql 中编写一个表值函数来返回 ids 并在代码中对该函数进行建模,假设它的名称是
fnGetIds
。
然后在您的代码中使用它,如下所示
var QueryOutput = (from x in TheDataContext.SomeTable
where TheDataContext.fnGetIds().Contains(x.ID) &&
x.Col1.Contains(SomeString) ||
x.Col2.Contains(SomeString))
select x.ID).ToList();
使用 2 个 where 子句:
List<long> TheTableIDs = list of IDs (sometimes more than 2100)
var _QueryOutput = (from x in TheDataContext.SomeTable
where x.Col1.Contains(SomeString) || x.Col2.Contains(SomeString))
select x.ID).ToList();
var QueryOutput = _QueryOutput.Where(w => TheTableIDs.Contains(w)).ToList();
为了提高效率,您可以重构代码,以便仅在列表包含超过 2000 个时才这样做:
if (TheTableIDs.Count() > 2000)
// Code Here
else
// Code Here
我得到了这个,我无法使用 2100 个参数!还有其他事情正在发生。
经过仔细检查,我发现我在循环中添加了 5 个参数,但源对象没有被清除,因此要插入的对象列表变得越来越大。
Dim reportNum = 1
For Each report In wwo.wwoWeatherReport.listOfForecasts
'whack these into the regionsAndCountries
db.addParameter("@forecastAirTemp" & reportNum, report.listOfhourlyForecasts(0).hourlyForecast_tempC)
db.addParameter("@forecastRainFall" & reportNum, report.listOfhourlyForecasts(0).hourlyForecast_precipMM)
reportNum = reportNum + 1
Next
我必须事先调暗一个新的 wwo 对象并对其进行排序
Dim wwo As New wwwoManager
Dim reportNum = 1 ...
这里的问题是使用 Contains ,它需要在单独的查询中:
List<long> TheTableIDs = list of IDs (sometimes more than 2100)
var QueryOutput = (from x in TheDataContext.SomeTable
where TheTableIDs.Contains(x.ID)
select x.ID).ToList();
foreach(var s in QueryOutput){
if(x.Col1.Contains(SomeString) || x.Col2.Contains(SomeString)){
//do something
}
}
我的解决方案是使用用户定义表来传入一组数据作为参数。
实体框架核心相当简单:
public IQueryable<Item> GetItemsByIds(IEnumerable<long> itemIds)
{
var items = this.DbContext.Items.FromSql(
GetCommand(),
GetParameters(itemIds));
return items;
}
public static string GetCommand()
{
return $"EXEC CommandName @ItemIds";
}
public static SqlParameter[] GetParameters(IEnumerable<long> itemIds)
{
var ids = new DataTable();
ids.Columns.Add("Id", typeof(long));
foreach (var itemId in itemIds)
{
ids.Rows.Add(itemId);
}
var itemIdsParam = new SqlParameter("@ItemIds", SqlDbType.Structured)
{
Value = ids,
TypeName = "[ItemDbo].[udtID]"
};
var paramList = new List<SqlParameter>
{
itemIdsParam
};
return paramList.ToArray();
}
当然,您必须首先在sql server中创建UDT和存储过程。
如果将
TheTableIDs
更改为IQueryable<long>
类型,问题就可以解决。
您应该修改生成
TheTableIDs
的查询。
例如使用以下代码:
var TheTableIDs = db.SomeTable.Select(d => d.ID);
而不是:
var TheTableIDs = db.SomeTable.Select(d => d.ID).AsEnumerable();
更改了此查询:
var z = (from b in db.GradeChangeHistories
where m_list.Contains(b.SessionKey)
select b);
为此,它有效:
var z = (from b in db.GradeChangeHistories
select b).ToList().Where(x => m_list.Contains(x.SessionKey));