Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 多个查找的推荐编程模式_C#_Sql_Database_Linq_Data Access - Fatal编程技术网

C# 多个查找的推荐编程模式

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文件和300多个独立但结构相同的CRM数据库之间创建数据同步过程。所有CRM数据库都在同一SQL Server实例中定义。具体情况如下:

源数据将是一个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