我的任务是在另一个供应商生成的CSV文件与超过300个独立但结构相同的CRM数据库之间创建数据同步过程。所有CRM数据库都在同一SQL Server实例中定义。以下是具体内容:
源数据将是一个CSV,其中包含客户选择加入营销传播的所有电子邮件地址列表。此CSV文件将每晚完整发送,但将包含记录级日期/时间戳,这将允许我仅选择自上次处理周期以来已修改的记录。 CSV文件可能会有数十万行,但每天的预期变化将大大低于此。
我将从CSV中选择数据,并将每行转换为自定义List<T>
对象。
查询CSV并转换数据后,我需要将此List<T>
的内容与CRM数据库进行比较。这是因为CSV文件中包含的任何给定电子邮件地址可能是:
如果主CSV列表中的电子邮件地址与任何CRM数据库之间存在匹配,则匹配的CRM记录将使用CSV文件中包含的值进行更新。
在一个很高的,非常通用的层面上,我认为我必须做这样的事情:
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
}
}
}
这是最有效的方法吗?我不想热心数百次访问300个数据库,以查看主CSV列表中的每个电子邮件是否都存在。理想情况下,我希望按照以下方式生成SQL语句:
"SELECT * FROM EMAIL_TABLE WHERE EMAIL_ADDRESS IN(email1,email2, email3,...)"
这将允许对数据库执行单个查询,但我不知道这种方法是否会更好/更有效,特别是因为我必须动态生成SQL并且可能会打开它注射。
此方案中的最佳做法是什么?因为我每次都需要比较300个数据库,所以我正在寻找能够以最少的处理时间产生最佳结果的方法。在我的生产代码中,我将实现一个多线程方法,以便可以同时处理多个数据库,因此任何方法都需要是线程安全的。
答案 0 :(得分:1)
你似乎有正确的基本想法。为CSV中的每一行命中数据库一次会太慢。您可以通过LINQ创建“where in”语句,如下所示:
var addresses = GetEmailAddresses();
var entries = ctx.Entries.Where(e => addresses.Contains(e.EmailAddress));
但是,如果列表中的地址太多,则生成和评估查询需要很长时间。我建议将您的输入列表分成合理大小的批次(200个条目?),然后使用上面的技巧通过单个数据库检查来处理每个批处理。
一旦你有了这个工作,你可以尝试其他一些事情,看看他们是否在性能方面产生了可衡量的差异:
答案 1 :(得分:0)
您可以将csv列表对象的内容放入表值参数中。然后调用存储过程,传入TVP。然后,存储过程可以在300个数据库中运行游标并加入到您的表值参数(使用ad-hoc sql)。它基本上是一个迭代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