推荐的多个查找编程模式

时间:2014-06-17 19:43:27

标签: c# sql database linq data-access

我的任务是在另一个供应商生成的CSV文件与超过300个独立但结构相同的CRM数据库之间创建数据同步过程。所有CRM数据库都在同一SQL Server实例中定义。以下是具体内容:

源数据将是一个CSV,其中包含客户选择加入营销传播的所有电子邮件地址列表。此CSV文件将每晚完整发送,但将包含记录级日期/时间戳,这将允许我仅选择自上次处理周期以来已修改的记录。 CSV文件可能会有数十万行,但每天的预期变化将大大低于此。

我将从CSV中选择数据,并将每行转换为自定义List<T>对象。

查询CSV并转换数据后,我需要将此List<T>的内容与CRM数据库进行比较。这是因为CSV文件中包含的任何给定电子邮件地址可能是:

  • 300个数据库的任何中不存在
  • 存在于300个数据库中的一个
  • 存在于多个数据库中

如果主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个数据库,所以我正在寻找能够以最少的处理时间产生最佳结果的方法。在我的生产代码中,我将实现一个多线程方法,以便可以同时处理多个数据库,因此任何方法都需要是线程安全的。

2 个答案:

答案 0 :(得分:1)

你似乎有正确的基本想法。为CSV中的每一行命中数据库一次会太慢。您可以通过LINQ创建“where in”语句,如下所示:

var addresses = GetEmailAddresses();
var entries = ctx.Entries.Where(e => addresses.Contains(e.EmailAddress));

但是,如果列表中的地址太多,则生成和评估查询需要很长时间。我建议将您的输入列表分成合理大小的批次(200个条目?),然后使用上面的技巧通过单个数据库检查来处理每个批处理。

一旦你有了这个工作,你可以尝试其他一些事情,看看他们是否在性能方面产生了可衡量的差异:

  1. 调整批量大小。
  2. 以不同程度的并行度独立运行批次。
  3. 使用数据库表上的索引进行播放,尤其是在电子邮件地址字段中。
  4. 在将电子邮件地址分成批次之前订购电子邮件地址。数据库查询可能会更好地利用硬盘缓存策略。

答案 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