c# - 是否可以通过复合主键列表从数据库中选择巨大的列表
问题描述
我有 2 个数据库。从我的数据库中,我正在获取列表项(我按日期获取,最多可以有 300 000 个元素)
public class Item
{
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
public DateTime Date { get; set; }
}
在其他数据库中(我不控制那个数据库,我可以从中读取,我无法更改这个数据库中的任何内容)我需要选择列表
public class OtherDbItem
{
public string X { get; set; }
public string Y { get; set; }
public string Z { get; set; }
public string FewOtherProperties { get; set; }
}
其中 X、Y、Z 是主键,我需要选择所有 otherDbItems 其中 Item.A = OtherDbItem.X 和 Item.B = OtherDbItem.Y 和 Item.C = OtherDbItem.Z (而不是将 OtherDbItems 映射到我的模型并保存在我的数据库)。
我正在使用 2 个不同的 EF Core DbContext 来连接数据库。
我试过了:
var otherDbItems = new List<OtherDbItem>();
foreach (var item in Items)
{
var otherDbItem = await this.context.OtherDbItems.FindAsync(item.A, item.B, item.C);
if (otherDbItem != null)
{
otherDbItems.Add(otherDbItem);
}
}
return otherDbItems;
但这可能是 300 000 个项目,因此对数据库有 300 000 个请求,显然这不是最佳的,也不可接受。
我也试过:
var ids = items.Select(item => item.A + item.B + item.C).ToList();
var otherDbItems = await this.context.OtherDbItems.Where(otherDbItem => ids.Contains(otherDbItem.X + otherDbItem.Y + otherDbItem.Z)).ToListAsync();
但这会导致大量的 sql 查询,速度很慢,并导致 ConnectionTimeOut。
是否可以快速可靠地获得 OtherDbItems?我必须得到这个项目的部分吗?例如 .take(1000).skip(0) 1 次通话中的项目?如果是,这部分应该有多大?
解决方案
我不能肯定这是最好的方法,因为我不是 EF 专家,但我最近遇到了类似的情况,我正在处理来自外部 JSON 导出到 EF Core 数据库的同步。该操作的一部分是验证如果导出更改,将基于导入数据增长的现有 EF Core 条目仍然有效,这足以说明随着数据库增长到必须验证的大约一百万条记录,我们遇到了超时和昂贵的查询问题。
我们最终采用的方法实际上最终提高了我们原始流程的速度,即批量操作。我们所做的与方法不同的一件事take()skip()
是我们实际上在输入端进行了批处理。本质上,我们一次收集 1000 个 id 并将其用于查询,然后再进行下一个查询。因此,您的代码/数据可能看起来像这样:
int chunkIndex = 0;
int batch = 1000;
var ids = items.Select(item => item.A + item.B + item.C).ToList();
while (chunkIndex < ids.Count)
{
var chunkIDs = ids.GetRange(chunkIndex,
chunkIndex + batch >= ids.Count ? ids.Count - chunkIndex : batch);
var otherDbItems = await this.context.OtherDbItems.Where(otherDbItem => chunkIDs.Contains(otherDbItem.X + otherDbItem.Y + otherDbItem.Z)).ToListAsync();
chunkIndex += batch;
}
因此,我认为这使您的查询成本更低,因为它不必运行整个事情然后限制结果,但是您的情况略有不同的是,您的源也是一个数据库,而我们的源是 JSON 内容。您可能可以通过在源表中使用take()
查询的方法来进一步优化这一点。这方面的语法可能不是 100% 正确,但也许这给出了这个想法:ids
Items
int chunkIndex = 0;
int batch = 1000;
// Update dbItemsContext.Items to your source context and table
int totalRecords = dbItemsContext.Items.Count();
while (chunkIndex < totalRecords)
{
// Update dbItemsContext.Items to your source context and table
var chunkIDs = dbItemsContext.Items.Select(item => item.A + item.B + item.C).Take(batch).Skip(chunkIndex).ToList();
var otherDbItems = await this.context.OtherDbItems.Where(otherDbItem => chunkIDs.Contains(otherDbItem.X + otherDbItem.Y + otherDbItem.Z)).ToListAsync();
chunkIndex += batch;
}
我希望这有助于展示我们的方法,但我认为这条路线您需要锁定表以避免更改,直到您的操作完成。我欢迎任何反馈,因为它也可以改进我们的流程。我还要注意,我们的应用程序/上下文未设置为异步运行,因此您可能需要进行一些额外的修改,或者甚至可能让这些批处理为您的用例异步运行。
关于批量大小的最后说明:您可能需要稍微尝试一下。我们的查询要复杂得多,所以 1000 似乎是我们的最佳选择,但您一次可以多取一点。我不确定是否有任何其他方法可以确定最佳批量大小,而不仅仅是测试一些不同的大小。
推荐阅读
- java - RX Java- 使用 java var args 压缩 n 个 Observables
- presto - 设置将使用 INSERT 语句写入的文件数
- tensorflow - TensorFlow GPU 设备“无法获取设备属性”
- dynamics-crm - 如何在 Dynamics 365 手机应用程序上隐藏模型驱动应用程序?
- c# - 将 datagridview 行插入 SQL 表
- python-3.x - pyspark dstream 中的 distinct() 等价物是什么?
- ruby - 试图从包含多个字符串的日志文件中获取数据
- java - 循环不会重新启动或重新加载
- performance - Spark 性能监控
- c# - 存储和处理 RPG 武器、法术、类 C#