C# 多个查找的推荐编程模式
我的任务是在另一家供应商生成的CSV文件和300多个独立但结构相同的CRM数据库之间创建数据同步过程。所有CRM数据库都在同一SQL Server实例中定义。具体情况如下: 源数据将是一个CSV,其中包含客户选择进行营销传播的所有电子邮件地址的列表。该CSV文件将每晚全部发送,但将包含记录级别的日期/时间戳,这将允许我仅选择自上次处理周期以来已修改的记录。CSV文件可能有数十万行,尽管每天的预期更改将大大低于此值 我将从CSV中选择数据,并将每一行转换为自定义的C# 多个查找的推荐编程模式,c#,sql,database,linq,data-access,C#,Sql,Database,Linq,Data Access,我的任务是在另一家供应商生成的CSV文件和300多个独立但结构相同的CRM数据库之间创建数据同步过程。所有CRM数据库都在同一SQL Server实例中定义。具体情况如下: 源数据将是一个CSV,其中包含客户选择进行营销传播的所有电子邮件地址的列表。该CSV文件将每晚全部发送,但将包含记录级别的日期/时间戳,这将允许我仅选择自上次处理周期以来已修改的记录。CSV文件可能有数十万行,尽管每天的预期更改将大大低于此值 我将从CSV中选择数据,并将每一行转换为自定义的列表对象 查询CSV并转换数据后,
列表
对象
查询CSV并转换数据后,我需要将此列表的内容与CRM数据库进行比较。这是因为CSV文件中包含的任何给定电子邮件地址可能:
- 300个数据库中的任何一个都不存在
- 存在于300个数据库之一
- 存在于多个数据库中
在任何情况下,如果主CSV列表中的电子邮件地址与任何CRM数据库之间存在匹配,则将使用CSV文件中包含的值更新匹配的CRM记录
在一个高的、非常普通的层面上,我想我必须做这样的事情:
foreach(string dbName in masterDatabaseList)
{
//open db connection
foreach(string emailAddress in masterEmailList)
{
//some helper method that would execute a SQL statement like
//"IF EXISTS ... WHERE EMAIL_ADDRESS = <emailAddress>" return true;
bool matchFound = EmailExistsInDb(emailAddress)
if (matchFound )
{
//the current email from the master list does exist in this database
//do necessary updates and stuff
}
}
}
这将允许对数据库执行单个查询,但我不知道这种方法是否会更好/更有效,特别是因为我必须动态生成SQL,并且可能会将其打开以进行注入
这种情况下的最佳实践是什么?因为我有300个数据库需要每次进行比较,所以我正在寻找一种方法,该方法将以最少的处理时间产生最好的结果。在我的产品代码中,我将实现一种多线程方法,以便可以同时处理多个数据库,因此任何方法都必须是线程安全的。您的基本想法似乎是正确的。在CSV中,每行点击一次数据库会太慢。您可以通过LINQ创建“where in”语句,如下所示:
var addresses = GetEmailAddresses();
var entries = ctx.Entries.Where(e => addresses.Contains(e.EmailAddress));
但是,如果列表中的地址太多,则需要很长很长时间才能生成和评估查询。我建议您将输入列表分成大小合理的批(200个条目?),然后使用上面的技巧通过单个数据库检查处理每个批
一旦这些措施奏效,您可以尝试其他一些方法,看看它们是否能在性能方面产生可测量的差异:
调整批量大小
以不同的并行度独立运行批处理
使用数据库表上的索引,特别是电子邮件地址字段上的索引
在将电子邮件地址分批之前,请先对其进行排序。db查询可能会更好地利用硬盘缓存策略
您可以将csv列表对象的内容放入表值参数中。然后调用一个存储过程,传入该TVP。然后,存储过程可以在300个数据库中运行游标,并连接到表值参数(使用特殊sql)。它将基本上是一个循环,迭代300次,这并不太糟糕。
大概是这样的:
foreach(string dbName in masterDatabaseList)
{
//open db connection
foreach(string emailAddress in masterEmailList)
{
//some helper method that would execute a SQL statement like
//"IF EXISTS ... WHERE EMAIL_ADDRESS = <emailAddress>" return true;
bool matchFound = EmailExistsInDb(emailAddress)
if (matchFound )
{
//the current email from the master list does exist in this database
//do necessary updates and stuff
}
}
}
您还可以利用.NET framework较新版本中的async
和wait
功能。您可以一次(异步)点击多个Dbs,但我不建议您尝试一次点击300。谢谢您的回复。我将来肯定会研究一些方法。当然,当我发布这个问题时,客户马上回来并更改了他们的要求——因此不再需要这个复杂的查找过程。数字:)这是一个非常有趣的方法,我没有真正考虑过。我总是喜欢在代码中做这样的事情,因为我觉得我对调试、错误处理和日志记录等有更多的控制权——但这可能值得在将来进行研究,以便与我通常的做法进行比较。
CREATE PROCEDURE yourNewProcedure
(
@TableValueParameter dbo.udtTVP READONLY
)
AS
DECLARE @dbName varchar(255)
DECLARE @SQL nvarchar(3000)
DECLARE DB_Cursor CURSOR LOCAL FOR
SELECT DISTINCT name
FROM sys.databases
WHERE Name like '%yourdbs%'
OPEN DB_Cursor
FETCH NEXT FROM DB_Cursor INTO @dbName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @SQL = 'UPDATE t
SET t2.Field = t.Field
FROM @TableValueParameter t
JOIN [' + @dbName + ']..TableYouCareAbout t2 ON t.Field = t2.Field '
EXEC sp_executesql @SQL, N'@TableValueParameter dbo.udtTVP', @TableValueParamete
FETCH NEXT FROM DB_Cursor INTO @dbName
END
CLOSE DB_Cursor
DEALLOCATE DB_Cursor