【问题标题】:Benefit of using Set Based Query over RBAR Query使用基于集合的查询优于 RBAR 查询的好处
【发布时间】:2018-04-12 05:22:41
【问题描述】:

首先,我为我的英语不好而道歉。

问题是——

在我的数据库中,我有一个包含 307763 行的表。每天我都需要处理每一行。为了处理这些行,我在数据库中编写了一个 SQL 查询来选择、插入和更新。查询的执行时间是 30 分钟1 小时强>.

然后我制作了一个 Visual Studio 控制台应用程序来运行该进程。

问题出现了。在 App.Config 中,数据库连接超时有 30 秒的时间限制。我还尝试将值设为 0。

是否有任何安全的方法可以使连接保持打开 1 小时或任何其他方法来解决问题?

这里是查询:

DECLARE @totalEmployee AS INT;
DECLARE @i AS INT=1;
DECLARE @employee TABLE
(
    Id INT PRIMARY KEY IDENTITY(1,1),
    SystemId VARCHAR(30),
    PreRecruitmentEmployeeId VARCHAR(30),
    DOJ DATETIME,
    DOS DATETIME,
    ProbationConfirmEntryDate DATETIME
)

INSERT INTO @employee
SELECT EI.SystemId, EI.PreRecruitmentEmployeeId, EI.DOJ, EI.DOS, EI.ProbationConfirmEntryDate FROM EmployeeInformation EI WHERE EI.EmployeeStatus='Active';
SELECT @totalEmployee=COUNT(*) FROM @employee;
WHILE @i<= @totalEmployee
  BEGIN
    UPDATE EmployeeDocument SET 
           DueProcessDateTime=CASE WHEN ED.DueProcessDateTime IS NULL THEN GETDATE() ELSE ED.DueProcessDateTime END, 
           IsMailSend=CASE WHEN ED.DueProcessDateTime IS NULL THEN 1 ELSE ED.IsMailSend END,
           DueDate=
            CASE 
                WHEN CD.DependateDate='AsAndWhen' THEN NULL
                WHEN CD.DependateDate='AppointmentDate' THEN 
                    CASE WHEN E.PreRecruitmentEmployeeId IS NULL AND E.DOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOJ) ELSE
                        CASE WHEN PRE.ApprovedDateTime IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, PRE.ApprovedDateTime) 
                        END
                    END
                WHEN CD.DependateDate='AgreedJoinDate' THEN
                    CASE WHEN E.PreRecruitmentEmployeeId IS NULL AND E.DOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOJ) ELSE
                        CASE WHEN PRE.AgreedDOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, PRE.AgreedDOJ) 
                        END
                    END
                WHEN CD.DependateDate='ResignationApplyDate' THEN (SELECT TOP(1) CASE WHEN ResignationDate<>'' THEN DATEADD(DAY, CD.LeadOrLagDays,ResignationDate)
                                                                    ELSE NULL END FROM TRN.Resignation WHERE EmployeeId=E.SystemId  ORDER BY ResignationDate DESC)
                WHEN CD.DependateDate='ApprovedResignationEffectiveDate' THEN (SELECT TOP(1) CASE WHEN ApprovedEffectiveDate<>'' THEN DATEADD(DAY, CD.LeadOrLagDays,ApprovedEffectiveDate)
                                                                    ELSE NULL END FROM TRN.Resignation WHERE EmployeeId=E.SystemId  ORDER BY ApprovedEffectiveDate DESC)
                WHEN CD.DependateDate='JoiningDate' AND E.DOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOJ)
                WHEN CD.DependateDate='LetterOfIndentDate' THEN 
                    CASE WHEN E.PreRecruitmentEmployeeId IS NULL AND E.DOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOJ) ELSE
                        CASE WHEN PRE.SelectionDateTime IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, PRE.SelectionDateTime) 
                        END
                    END
                WHEN CD.DependateDate='ProfileSubmit' THEN 
                    CASE WHEN E.PreRecruitmentEmployeeId IS NULL AND E.DOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOJ) ELSE
                        CASE WHEN PRE.SelectionDateTime IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, PRE.SelectionDateTime) 
                        END
                    END
                WHEN CD.DependateDate='ProbitionPeriodConfirmationDate' AND E.ProbationConfirmEntryDate IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.ProbationConfirmEntryDate)
                WHEN CD.DependateDate='PromotionDate' THEN NULL
                WHEN CD.DependateDate='SeparationDate' AND E.DOS IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOS)
                WHEN CD.DependateDate='SelectionDate' THEN
                CASE WHEN E.PreRecruitmentEmployeeId IS NULL AND E.DOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOJ) ELSE
                        CASE WHEN PRE.SelectionDateTime IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, PRE.SelectionDateTime) 
                        END
                    END
            END
        FROM EmployeeDocument ED
        JOIN HKP.ComplianceDocument CD ON CD.Id=ED.ComplianceDocumentId
        LEFT JOIN @employee E ON E.SystemId=ED.EmpSystemID
        LEFT JOIN PreRecruitmentEmployee PRE ON PRE.Id=E.PreRecruitmentEmployeeId
        WHERE E.Id=@i AND ED.FileId IS NULL                     
    SET @i = @i + 1;
  END

【问题讨论】:

  • 把相关代码分享给你会更好
  • 连接超时并不表示连接可以保持打开多长时间。它表示连接尝试连接数据库可以花费多少时间。
  • 你应该问诸如“如何最小化执行时间?”之类的问题。而不是增加连接时间。我认为你可以在 web config 和 Sqlcommand 超时中增加连接时间。除此之外,你可以在 conn 字符串中使用池。
  • 一个好问题是独立的。不是每个人都可以访问隐藏在链接后面的内容。您应该在问题中包含minimal reproducible example。在 SQL 相关问题中,通常包含相关表 DDL、作为 DML 的示例数据以及期望的结果。在还包括执行计划的 SQL 性能问题中。
  • 我已经为您编辑了您的问题,以便将 SQL 语句作为格式化文本包含在内。下次请自己做。您的问题可能是您的 SQL 语句在该循环中使用了 RBAR 方法。如果用基于集合的方法重写它会运行得更快。

标签: c# sql .net sql-server database


【解决方案1】:

现在我们可以看到您的 sql 语句,我们可以提出更好的方法。

您当前的 SQL 语句正在使用循环,这意味着它@Employee 表变量中的每条记录运行一次。这种程序方法被称为 RBAR -(RBAR 发音为“ree-bar”,是“Row-By-Agonizing-Row”的“现代主义” - 由 Jeff Moden 创造 - SQL 大师) - 因为它是,嗯,慢得令人痛苦。 (这里是one of many articles 了解更多信息)

SQL 不适用于这种方法。相反,它适用于基于集合的方法 - 这意味着您让它在一组记录上工作,而不是逐个处理。

我认为你的 sql 语句应该是这样的(注意,我已经用一个简单的派生表替换了 @Employees 表变量,因此无需担心)。

请注意我无法对此进行测试,因为您没有提供样本数据也没有提供所需的结果,因此您需要自己进行测试 - 但原则是 - 当您可以避免。

UPDATE EmployeeDocument 
SET DueProcessDateTime = CASE WHEN ED.DueProcessDateTime IS NULL THEN GETDATE() ELSE ED.DueProcessDateTime END, 
    IsMailSend = CASE WHEN ED.DueProcessDateTime IS NULL THEN 1 ELSE ED.IsMailSend END,
    DueDate = 
        CASE 
        WHEN CD.DependateDate='AsAndWhen' THEN NULL
        WHEN CD.DependateDate='AppointmentDate' THEN 
            CASE WHEN E.PreRecruitmentEmployeeId IS NULL AND E.DOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOJ) ELSE
                CASE WHEN PRE.ApprovedDateTime IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, PRE.ApprovedDateTime) 
                END
            END
        WHEN CD.DependateDate='AgreedJoinDate' THEN
            CASE WHEN E.PreRecruitmentEmployeeId IS NULL AND E.DOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOJ) ELSE
                CASE WHEN PRE.AgreedDOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, PRE.AgreedDOJ) 
                END
            END
        WHEN CD.DependateDate='ResignationApplyDate' THEN (SELECT TOP(1) CASE WHEN ResignationDate<>'' THEN DATEADD(DAY, CD.LeadOrLagDays,ResignationDate)
                                                            ELSE NULL END FROM TRN.Resignation WHERE EmployeeId=E.SystemId  ORDER BY ResignationDate DESC)
        WHEN CD.DependateDate='ApprovedResignationEffectiveDate' THEN (SELECT TOP(1) CASE WHEN ApprovedEffectiveDate<>'' THEN DATEADD(DAY, CD.LeadOrLagDays,ApprovedEffectiveDate)
                                                            ELSE NULL END FROM TRN.Resignation WHERE EmployeeId=E.SystemId  ORDER BY ApprovedEffectiveDate DESC)
        WHEN CD.DependateDate='JoiningDate' AND E.DOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOJ)
        WHEN CD.DependateDate='LetterOfIndentDate' THEN 
            CASE WHEN E.PreRecruitmentEmployeeId IS NULL AND E.DOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOJ) ELSE
                CASE WHEN PRE.SelectionDateTime IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, PRE.SelectionDateTime) 
                END
            END
        WHEN CD.DependateDate='ProfileSubmit' THEN 
            CASE WHEN E.PreRecruitmentEmployeeId IS NULL AND E.DOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOJ) ELSE
                CASE WHEN PRE.SelectionDateTime IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, PRE.SelectionDateTime) 
                END
            END
        WHEN CD.DependateDate='ProbitionPeriodConfirmationDate' AND E.ProbationConfirmEntryDate IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.ProbationConfirmEntryDate)
        WHEN CD.DependateDate='PromotionDate' THEN NULL
        WHEN CD.DependateDate='SeparationDate' AND E.DOS IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOS)
        WHEN CD.DependateDate='SelectionDate' THEN
            CASE WHEN E.PreRecruitmentEmployeeId IS NULL AND E.DOJ IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, E.DOJ) ELSE
                CASE WHEN PRE.SelectionDateTime IS NOT NULL THEN DATEADD(DAY, CD.LeadOrLagDays, PRE.SelectionDateTime) 
                END
            END
        END
FROM EmployeeDocument ED
JOIN HKP.ComplianceDocument CD ON CD.Id=ED.ComplianceDocumentId
LEFT JOIN 
(
    SELECT EI.SystemId, EI.PreRecruitmentEmployeeId, EI.DOJ, EI.DOS, EI.ProbationConfirmEntryDate 
    FROM EmployeeInformation EI 
    WHERE EI.EmployeeStatus='Active'
) E ON E.SystemId=ED.EmpSystemID
LEFT JOIN PreRecruitmentEmployee PRE ON PRE.Id=E.PreRecruitmentEmployeeId
WHERE ED.FileId IS NULL                     

【讨论】:

  • @Abdullah-Al-Mamun,Zohar 的答案是朝着正确的方向。即使输出不正确,也可以用同样的方法纠正。毫无疑问,您的查询需要进一步改进
  • @KumarHarsh 谢谢!
  • 很高兴为您提供帮助 :-) 顺便说一句,您的英语对我来说似乎很好(尽管我自己的母语不是英语 :-))
猜你喜欢
  • 2018-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-25
  • 2023-04-01
相关资源
最近更新 更多