【问题标题】:Advice on software / database design to avoid using cursors when updating database关于软件/数据库设计的建议,以避免在更新数据库时使用游标
【发布时间】:2011-02-21 19:06:38
【问题描述】:

我有一个数据库,用于记录员工参加课程的时间以及他们下次参加课程的时间(课程往往是年度课程)。

例如,以下员工于 2010 年 1 月 1 日参加了课程“1”,由于该课程是年度课程,因此下一次课程将于 2011 年 1 月 1 日参加。今天是 2010 年 5 月 20 日,课程状态显示为“完成' 即他们已经完成了课程并且不需要再做一次,直到明年:

EmployeeID    CourseID    AttendanceDate    DueDate     Status
123456        1           01/01/2010        01/01/2011  Complete

DueDate 而言,当我更新员工记录时,我会在 SQL 中进行计算,例如DueDate = AttendanceDate + CourseFrequency(我从单独的表中提取课程频率)。

在我的基于 Web 的应用程序 (asp.net mvc) 中,我为所有员工提取了这些数据,并以类似网格的格式显示,供 HR 经理查看。这使 HR 可以确定哪些人需要参加课程。

我遇到的问题如下。

以上面的例子为例,假设今天是 2011 年 1 月 2 日。在这种情况下,员工 123456 现在已逾期,我想将 Status 设置为未完成,以便人力资源经理可以看到他们需要采取行动,即让员工参与课程。

我可以在数据库中构建一个触发器以在夜间运行,以根据当前日期更新所有员工的Status 字段。根据我的阅读,我需要使用 cursors 遍历每一行以修改状态,这被认为是不好的做法/效率低下,或者至少可以避免一些事情???

或者,我可以在从数据库中提取数据之后并在屏幕上显示之前在我的 C# 代码中计算 Status。问题是数据库中的Status 不一定与屏幕上显示的内容相匹配,这对我来说是完全错误的。

有人对解决此类问题的最佳实践方法有任何建议吗?

这很有帮助,如果我确实使用了游标,我怀疑我会在任何给定时间循环超过 1000 条记录。可能体积这么小,用游标就可以了?

【问题讨论】:

    标签: sql-server cursor


    【解决方案1】:

    除非我在您的解释中遗漏了什么,否则根本不需要游标:

    UPDATE
        dbo.YourTable
    SET
        Status = ‘Incomplete’
    WHERE
        DueDate < GETDATE()
    

    最好不要为这些记录维护 DueDate 或 Status。我希望看到 Employee、Course、EmployeeCourse 和 EmployeeCourseAttendance 表,您可以使用以下表:

    -- Employees that haven't attended a course 
    -- within dbo.Course.Frequency of current date
    SELECT
        ec.EmployeeID
        , ec.CourseID
        , eca.LastAttendanceDate
        , DATEADD(day, c.Frequency, eca.LastAttendanceDate) AS DueDate
    FROM
        dbo.EmployeeCourse ec
    INNER JOIN
        dbo.Course c
    LEFT OUTER JOIN
        ebo.EmployeeCourseAttendance eca
    ON  eca.EmployeeID = ec.EmployeeId
    AND eca.CourseID = ec.CourseID
    WHERE
        GETDATE() > DATEADD(day, c.Frequency, eca.LastAttendanceDate)
    
    -- Show all employees and status for each course
    SELECT
        ec.EmployeeID
        , ec.CourseID
        , eca.LastAttendanceDate
        , DATEADD(day, c.Frequency, eca.LastAttendanceDate) AS DueDate
        , CASE
            WHEN eca.LastAttendanceDate IS NULL THEN 'Has not attended'
            WHEN (GETDATE() > DATEADD(day, c.Frequency, eca.LastAttendanceDate) THEN 'Incomplete'
            WHEN (GETDATE() < DATEADD(day, c.Frequency, eca.LastAttendanceDate) THEN 'Complete'
          END AS Status
    FROM
        dbo.EmployeeCourse ec
    INNER JOIN
        dbo.Course c
    LEFT OUTER JOIN
        ebo.EmployeeCourseAttendance eca
    ON  eca.EmployeeID = ec.EmployeeId
    AND eca.CourseID = ec.CourseID
    

    【讨论】:

    • 我刚回来更新我的问题,说我现在意识到我根本不需要游标,但你打败了我!就像您关于不维护到期日期或状态的想法一样 - 我想我会尝试为此更新我的 sql。非常感谢。
    • 跟进问题 - 您是否建议在数据库中没有状态和到期日期,因为您认为这是一种更简洁的做事方式,或者因为它更有效,因为我不需要将数据存储在db 并且可以在我需要的时候即时获取这些数据?
    • 应避免存储可以从现有数据中轻松有效地派生的内容。在某些情况下,您希望完全相反,但起点应该始终是正常化。建议阅读关系理论和规范化的介绍,应该有助于澄清为什么您的原始计划可能是一个坏主意。
    【解决方案2】:

    您也可以使用计算列表达式。这样您就不必更新 STATUS 列/使其与日期同步

    create table coureses
    (
    employeeid int not null,
    courseid int not null,
    attendancedate datetime null,
    duedate datetime null,
    [status] as case
        when duedate is null and attendancedate is null then 'n/a'
        when datediff(day,duedate, getdate()) > 0 then 'Incomplete'
        when datediff(day,attendancedate, getdate()) > 0 then 'Complete'
        else 'n/a'
        end
    )
    

    【讨论】:

      【解决方案3】:

      我不会为此使用触发器,而是安排一个作业,可能在 SQL Server 中。这里也不需要游标,肯定只是:

      UPDATE TABLE SET Status = 'Incomplete' WHERE DueDate < GetDate()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多