【问题标题】:Recommended programming pattern for multiple lookups推荐的多次查找编程模式
【发布时间】:2014-08-07 22:52:40
【问题描述】:

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

【问题讨论】:

    标签: c# sql database linq data-access


    【解决方案1】:

    您的基本想法似乎是对的。为 CSV 中的每一行访问数据库一次太慢了。您可以像这样通过 LINQ 创建“where in”语句:

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

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

    一旦你完成了这项工作,你可以尝试其他一些方法,看看它们是否会在性能方面产生可衡量的差异:

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

    【讨论】:

    • 您还可以利用较新版本的 .NET 框架中的 asyncawait 功能。您可以一次(异步)点击多个 Db,但我不一定建议尝试一次达到 300 个。
    • 感谢您的回复。我将来肯定会研究一些方法。当然,我一发布这个问题,客户就回来并改变了他们的要求——因此不再需要这个复杂的查找过程。图。 :)
    【解决方案2】:

    您可以将 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
    

    【讨论】:

    • 这是一种非常有趣的方法,而且我还没有真正考虑过。我一直更喜欢在代码中做这样的事情,因为我觉得我对调试、错误处理和日志记录等有更多的控制权——但这可能值得在未来进行研究,以将其与我通常的做事方式进行比较。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多