对 IN 语句使用具有大量值的 OracleDataReader
Use OracleDataReader with a large amount of values for an IN statement
目前,我正在尝试实现数据 reader 以执行特别大的查询。当前的实现使用 Entity Framework,但由于查询的性质,它非常慢(大约 4 分半钟)。
这是当前使用 EF 的实现:
public List<SomeDataModel> GetSomeData(List<string> SomeValues, string setId)
{
var ret = new List<SomeDataModel>();
using(var context = new SomeDBContext())
{
var data = context.SomeEntity.Where(x => x.SetId == setId && SomeValues.Contains(x.SomeValue));
data.ForEach(x => ret.Add(mapper.Map<SomeDataModel>(x))); // mapper is an instance of AutoMapper via dependency injection
}
return ret;
}
理想情况下,我想生成一个更基本的查询字符串并通过 OracleDataReader 提取数据。问题是:在 Oracle 的 IN
语句中,您只能有 1000 个值。 SomeValues
参数可以是 5,000 到 25,000 之间的任何值,所以我想在后端 EF 会自行生成多个查询,但正如我所说,它非常慢。
这就是我试图采取的方向:
public List<SomeDataModel> GetSomeData(List<string> SomeValues, string setId)
{
var ret = new List<SomeDataModel>();
const int MAX_CHUNK_SIZE = 1000;
var totalPages = (int)Math.Ceiling((decimal)SomeValues.Count / MAX_CHUNK_SIZE);
for(var i = 0; i < totalPages; i++)
{
var chunkItems = SomeValues.Skip(i * MAX_CHUNK_SIZE).Take(MAX_CHUNK_SIZE).ToList();
pageList.Add(chunkItems);
}
using (var context = new CASTDbContext())
{
var connStr = context.Database.Connection.ConnectionString;
using (var conn = new OracleConnection(connStr))
{
foreach(var page in pageList)
{
var queryStr = string.Format("SELECT * FROM SomeTable WHERE SomeColumn IN ({0})", "(" + string.Join(",", page.ToArray()) + ")");
var cmd = new OracleCommand(queryStr, conn);
using (var reader = cmd.ExecuteReader())
{
while(reader.Read())
{
var newItem = new SomeDataModel();
newItem.Something = reader["Something"].ToString();
ret.Add(newItem);
}
}
}
}
}
return ret;
}
我想期望的结果是为 reader 高效地生成多个查询,或者构造一个可以有效处理这种情况的查询。我在第二个示例中所拥有的是目前的占位符代码。
起初,
如果我们使用一对列,Oracle 可以在 IN
列表中获取超过 1000 个值。
所以,下面会抛出错误:
SELECT * FROM TABLE
WHERE COL1 IN (VAL1, VAL2,... VAL1000, VAL1001);
但以下内容会起作用:
SELECT * FROM TABLE
WHERE (COL,1) IN ( (VAL1,1), (VAL2,1),... (VAL1000,1),
(VAL1001,1).....(VAL9999,1) );
-- we have used pair of value and 1 to be compared with col and 1
希望,这会给你解决问题的方向。是的,我不确定性能,所以请最后检查一下。
干杯!!
可能有帮助的东西:
根据您的 SomeEntity 和 SomeDataModel 的外观,您可以从数据库中加载不需要的值,甚至可以跳过延迟加载,因为在填充数据模型时它引用了一个并不急切的相关实体加载。对于 Automapper,我建议在 Linq 查询中利用 ProjectTo
方法。这将确保数据命中仅发生一次,并且 returns 只是数据模型所需的字段。
var results = context.SomeEntity
.Where(x => x.SetId == setId && SomeValues.Contains(x.SomeValue))
.ProjectTo<SomeDataModel>()
.ToList();
或使用 Automapper 9:
var results = mapper.ProjectTo<SomeDataModel>(context.SomeEntity
.Where(x => x.SetId == setId && SomeValues.Contains(x.SomeValue)))
.ToList();
像这样查询大量任意值永远不会有效率。您还应该检查数据是否已编入索引。一旦您捕获 SQL 正在 运行 检索您的数据,将其通过合适的分析器(我对 Oracle 工具不是很熟悉)来查看是否有索引建议。
目前,我正在尝试实现数据 reader 以执行特别大的查询。当前的实现使用 Entity Framework,但由于查询的性质,它非常慢(大约 4 分半钟)。
这是当前使用 EF 的实现:
public List<SomeDataModel> GetSomeData(List<string> SomeValues, string setId)
{
var ret = new List<SomeDataModel>();
using(var context = new SomeDBContext())
{
var data = context.SomeEntity.Where(x => x.SetId == setId && SomeValues.Contains(x.SomeValue));
data.ForEach(x => ret.Add(mapper.Map<SomeDataModel>(x))); // mapper is an instance of AutoMapper via dependency injection
}
return ret;
}
理想情况下,我想生成一个更基本的查询字符串并通过 OracleDataReader 提取数据。问题是:在 Oracle 的 IN
语句中,您只能有 1000 个值。 SomeValues
参数可以是 5,000 到 25,000 之间的任何值,所以我想在后端 EF 会自行生成多个查询,但正如我所说,它非常慢。
这就是我试图采取的方向:
public List<SomeDataModel> GetSomeData(List<string> SomeValues, string setId)
{
var ret = new List<SomeDataModel>();
const int MAX_CHUNK_SIZE = 1000;
var totalPages = (int)Math.Ceiling((decimal)SomeValues.Count / MAX_CHUNK_SIZE);
for(var i = 0; i < totalPages; i++)
{
var chunkItems = SomeValues.Skip(i * MAX_CHUNK_SIZE).Take(MAX_CHUNK_SIZE).ToList();
pageList.Add(chunkItems);
}
using (var context = new CASTDbContext())
{
var connStr = context.Database.Connection.ConnectionString;
using (var conn = new OracleConnection(connStr))
{
foreach(var page in pageList)
{
var queryStr = string.Format("SELECT * FROM SomeTable WHERE SomeColumn IN ({0})", "(" + string.Join(",", page.ToArray()) + ")");
var cmd = new OracleCommand(queryStr, conn);
using (var reader = cmd.ExecuteReader())
{
while(reader.Read())
{
var newItem = new SomeDataModel();
newItem.Something = reader["Something"].ToString();
ret.Add(newItem);
}
}
}
}
}
return ret;
}
我想期望的结果是为 reader 高效地生成多个查询,或者构造一个可以有效处理这种情况的查询。我在第二个示例中所拥有的是目前的占位符代码。
起初,
如果我们使用一对列,Oracle 可以在 IN
列表中获取超过 1000 个值。
所以,下面会抛出错误:
SELECT * FROM TABLE
WHERE COL1 IN (VAL1, VAL2,... VAL1000, VAL1001);
但以下内容会起作用:
SELECT * FROM TABLE
WHERE (COL,1) IN ( (VAL1,1), (VAL2,1),... (VAL1000,1),
(VAL1001,1).....(VAL9999,1) );
-- we have used pair of value and 1 to be compared with col and 1
希望,这会给你解决问题的方向。是的,我不确定性能,所以请最后检查一下。
干杯!!
可能有帮助的东西:
根据您的 SomeEntity 和 SomeDataModel 的外观,您可以从数据库中加载不需要的值,甚至可以跳过延迟加载,因为在填充数据模型时它引用了一个并不急切的相关实体加载。对于 Automapper,我建议在 Linq 查询中利用 ProjectTo
方法。这将确保数据命中仅发生一次,并且 returns 只是数据模型所需的字段。
var results = context.SomeEntity
.Where(x => x.SetId == setId && SomeValues.Contains(x.SomeValue))
.ProjectTo<SomeDataModel>()
.ToList();
或使用 Automapper 9:
var results = mapper.ProjectTo<SomeDataModel>(context.SomeEntity
.Where(x => x.SetId == setId && SomeValues.Contains(x.SomeValue)))
.ToList();
像这样查询大量任意值永远不会有效率。您还应该检查数据是否已编入索引。一旦您捕获 SQL 正在 运行 检索您的数据,将其通过合适的分析器(我对 Oracle 工具不是很熟悉)来查看是否有索引建议。