C# 使用从并行线程运行的Create index、table和procedure的SQL查询

C# 使用从并行线程运行的Create index、table和procedure的SQL查询,c#,sql,entity-framework-6,sql-server-2016,C#,Sql,Entity Framework 6,Sql Server 2016,我对从并行线程执行的查询有问题。 关于索引|表类型|过程|表已经存在,我几乎总是会出错 我非常确定我的sql查询是针对该错误进行保护的,它永远不会出现 此查询应该能够从并发线程运行,不会出现任何错误 我在做什么: 我有数百个文件,每个文件包含数百万行 每行包含我需要插入到特定表中的对象 如果表/索引/过程不存在,我需要创建它 目前,这是相当好的工作,除了这个错误,不应该出现 我的查询代码(创建索引时): } 我已经标记了(…)代码,其中实际上没有什么重要的事情发生——只是一些数据处理 DB是实体

我对从并行线程执行的查询有问题。 关于索引|表类型|过程|表已经存在,我几乎总是会出错

我非常确定我的sql查询是针对该错误进行保护的,它永远不会出现

此查询应该能够从并发线程运行,不会出现任何错误

我在做什么:

  • 我有数百个文件,每个文件包含数百万行

  • 每行包含我需要插入到特定表中的对象

  • 如果表/索引/过程不存在,我需要创建它

  • 目前,这是相当好的工作,除了这个错误,不应该出现

  • 我的查询代码(创建索引时):

    }

    我已经标记了(…)代码,其中实际上没有什么重要的事情发生——只是一些数据处理

    DB是实体框架6上下文,也包含此CreateIndex方法

    Parallel.ForEach(timeSeriesFiles, file =>
    {
        (...)
    
        // FOREACH LINE 
        foreach (string line in File.ReadLines(file))
        {
            (...)
            using (var db = Context.DB)
            {
                db.CreateIndex(tableName(line));
            }
            (...)
        }
    }
    

    如果您能提供任何建议或线索,我将不胜感激。

    您收到此错误,因为至少有两个线程试图同时创建索引/存储过程/表。我在您的SQL中没有看到任何可以防止此错误的内容。我建议实现某种锁定机制来处理此错误。您可以在SQL或代码中执行此操作-无论哪种方式,逻辑基本相同:

  • 检查索引是否存在。如果索引不存在,则获取锁。在SQL中,类似于:
    使用(TABLOCKX)从tableName中选择1

  • 一旦授予锁,检查索引是否不存在(是的,您需要检查两次。可能是另一个线程在您等待授予锁时创建了索引)

  • 释放锁


  • 您收到此错误是因为至少有两个线程试图同时创建索引/存储过程/表。我在您的SQL中没有看到任何可以防止此错误的内容。我建议实现某种锁定机制来处理此错误。您可以在SQL或代码中执行此操作-无论哪种方式,逻辑基本相同:

  • 检查索引是否存在。如果索引不存在,则获取锁。在SQL中,类似于:
    使用(TABLOCKX)从tableName中选择1

  • 一旦授予锁,检查索引是否不存在(是的,您需要检查两次。可能是另一个线程在您等待授予锁时创建了索引)

  • 释放锁

  • 使用事务。 对于exlamle,请修改脚本:

    sb.AppendFormat(@" 
    begin tran
    IF NOT EXISTS(SELECT * FROM sys.indexes 
        WHERE name='TimeSeries_DateStamp_{0}' AND object_id = OBJECT_ID('{0}'))
        exec(' CREATE UNIQUE CLUSTERED INDEX TimeSeries_DateStamp_{0} ON [dbo].[{0}]
            (
            [TimeSeriesID] ASC,
            [DateStamp]    ASC
            ) WITH (
            PAD_INDEX = OFF, 
            STATISTICS_NORECOMPUTE = OFF, 
            SORT_IN_TEMPDB = OFF, 
            IGNORE_DUP_KEY = OFF, 
            DROP_EXISTING = OFF, 
            ONLINE = OFF,
            ALLOW_ROW_LOCKS = ON, 
            ALLOW_PAGE_LOCKS = ON) 
            ON [PRIMARY] '); 
    commit", tableName);
    
    或者您可以使用

    使用事务。 对于exlamle,请修改脚本:

    sb.AppendFormat(@" 
    begin tran
    IF NOT EXISTS(SELECT * FROM sys.indexes 
        WHERE name='TimeSeries_DateStamp_{0}' AND object_id = OBJECT_ID('{0}'))
        exec(' CREATE UNIQUE CLUSTERED INDEX TimeSeries_DateStamp_{0} ON [dbo].[{0}]
            (
            [TimeSeriesID] ASC,
            [DateStamp]    ASC
            ) WITH (
            PAD_INDEX = OFF, 
            STATISTICS_NORECOMPUTE = OFF, 
            SORT_IN_TEMPDB = OFF, 
            IGNORE_DUP_KEY = OFF, 
            DROP_EXISTING = OFF, 
            ONLINE = OFF,
            ALLOW_ROW_LOCKS = ON, 
            ALLOW_PAGE_LOCKS = ON) 
            ON [PRIMARY] '); 
    commit", tableName);
    

    或者你也可以用我在史蒂佛和马克西姆·埃罗什金的帮助下最终取得的成就 是:

    创建新存储过程的代码(我遇到了最大的问题)

    重要的是要添加锁,对于存储过程BEGIN TRY和BEGIN CATCH也是如此。
    这是我找到的最好的方法。

    多亏了史蒂佛和马克西姆·埃罗什金,我终于实现了目标 是:

    创建新存储过程的代码(我遇到了最大的问题)

    重要的是要添加锁,对于存储过程BEGIN TRY和BEGIN CATCH也是如此。 这是我找到的最好的方法

    sb.AppendFormat(@"BEGIN TRAN
    IF NOT EXISTS ( SELECT * 
                                FROM sys.procedures AS T 
                                WITH (TABLOCKX)
                                INNER JOIN sys.schemas AS S  ON T.schema_id = S.schema_id
                                WHERE S.Name = 'dbo' AND T.Name = 'BulkMerge{0}' ) 
    BEGIN
    SELECT 1 FROM sys.procedures WITH (TABLOCKX)
    IF NOT EXISTS ( SELECT * 
                                FROM sys.procedures AS T 
                                WITH (TABLOCKX)
                                INNER JOIN sys.schemas AS S  ON T.schema_id = S.schema_id
                                WHERE S.Name = 'dbo' AND T.Name = 'BulkMerge{0}' ) 
    BEGIN TRY
              EXEC(' 
                CREATE PROCEDURE [dbo].[BulkMerge{0}]
                    @table [Bulk{0}] READONLY
                AS
                BEGIN
                    SET NOCOUNT ON;
    
                    MERGE INTO {0} t1
                    USING @table t2
                    ON t1.[TimeSeriesID] = t2.[TimeSeriesID] AND t1.[DateStamp] = t2.[DateStamp]
                    WHEN MATCHED 
                    THEN UPDATE SET {1} 
                    WHEN NOT MATCHED 
                    THEN INSERT VALUES (t2.TimeSeriesID, t2.DateStamp {2} ); 
                END
                ')
    END TRY BEGIN CATCH END CATCH
    END 
    COMMIT ", tableName, fieldsSB, insertSB);