【问题标题】:Complicated MySQL Stored Proceedure复杂的 MySQL 存储过程
【发布时间】:2009-03-24 14:49:27
【问题描述】:

我需要创建一个半复杂的存储过程。我在这里并不完全是一名 DBA,但我公司中没有其他人比我更好。基本思想是我有一组任务。用户完成这些任务并为每个完成的任务获得一个标志。要被视为“完成”,用户必须完成所有一组给定任务。关键在于,其中一些任务可能是其他任务组的包装。

例如,我们有以下可能的任务:

Task 1
Task 2 
Task 3
Task 4 -> (1, 2, 3)
Task 5 -> (1, 2)
Task 6
Task 7 -> (5, 6)

一旦用户完成了任务 1 和 2,他们就隐含地完成了任务 5。一旦他们完成了任务 3,他们就隐含地完成了任务 4。

如果另一个用户完成任务 1、2 和 6,他们将隐式完成 1、2、5、6、7。

反过来,如果一项作业只需要任务 7,那么它会扩展为需要任务 6 和 5,而这需要任务 1 和 2。

我有五个基本表:userCompletedTask、assignmentRequiredTask、compoundTask、userTaskAssignment 和 task。我省略了用户和分配表,因为它是多余的。

userCompletedTask:
userID (Foreign Key on user)
taskID (Foreign Key on task)

usertTaskAssignment:
userID (Foreign key on user)
assignmentID (Foreign key on assignment)

assignmentRequiredTask:
assignmentID (Foreign key on assignment)
taskID (Foreign key on task)

task:
taskID (primary key)
compound (boolean flag. If 1, it is compound)

compoundTask:
parentID (foreign key on task)
childID (foreign key on task)

为用户分配了一个 userTaskAssignment,需要完成任务 4。我想构建一个存储过程,将根据 assignmentRequiredTasks 检查 userCompletedTasks,检查是否有适当的复合任务。

伪代码如下:

collection tasksCompleted = user->getTasksCompleted
collection tasksRequired = new collection

foreach task in assignment->getRequiredTasks 
   if(task.isCompound) 
      tasksRequired->addAll(getCompountTasks(task))
   else 
      tasksRequired->add(task)

if tasksCompleted->containsAll(tasksRequired)
   return true
else 
   return false

我只是不太了解 MySQL/SQL 的内部结构,无法将其转换为存储过程。最后的手段是我将代码拉入应用程序,但这确实适合数据级别。任何帮助将不胜感激。

编辑

正如下面所指出的,compoundTask 条目本身可以是复合任务。因此,您将需要一个递归练习来获得所有需要完成的非复合任务的列表。我扩展了上面的例子来说明这一点。

【问题讨论】:

    标签: sql mysql stored-procedures


    【解决方案1】:

    真的,您的compoundTask 表正在复制您的task 表。如果您只是在任务表中添加一个可为空的parent_id 列,则可以在一个表中获得相同的结果。

    在这里,这是未经测试的,甚至可能不是有效的 MySQL,但它应该让你开始:

    SELECT DISTINCT taskID FROM task AS t LEFT JOIN compoundTask AS ct ON ct.taskID = t.taskID
    INNER JOIN userCompletedTask AS uct ON uct.taskID = t.taskID
    INNER JOIN userCompletedTask AS uctCompound ON uctCompound.taskID = ct.taskID
    WHERE uct.userID = @user AND uctCompound.userID = @user
    

    对于指定的@user,从这里返回的任何taskIDs 都已完成。

    自从我完成 MySQL 以来已经有很长时间了 - 所以这甚至可能行不通。此外,如果您可以合并compoundTasktask 表,则不需要第二个INNER JOIN

    【讨论】:

    • 创建尝试使用可为空的父行的问题是单个任务可能是多个复合任务的子任务。在示例中,task1 和 task2 都是复合任务 4 和 5 的成员。但这绝对让我开始。我从没想过双重加入同一张桌子
    • 其实你是对的——这个方法是行不通的,因为taskID是一个主键。您必须添加另一列作为主键,例如 CompoundTaskID... 示例行:[compoundTaskID: 1 - taskID: 5 - parentID: 1] - [compoundTaskID: 2 - taskID: 5 - parentID: 2]
    【解决方案2】:

    我是 mysql 的新手,但我对 T-SQL 存储过程并不陌生。这是我认为应该是存储过程。我认为它是 T-SQL 和 mysql 的混合体......所以请适当消除语法顾虑

    CREATE PROCEDURE proc (@userId BIGINT)
    AS
    BEGIN
    
    /*
       Consider this table the same as
       collection tasksCompleted = user->getTasksCompleted
    */
    CREATE TABLE #TasksCompleted
    (
       taskID BIGINT
    );
    
    INSERT INTO #TaskCompleted
       (SELECT
        task.taskId
        FROM
        userCompletedTask natural join
        task
        WHERE
        userCompletedTask.userId = @userID AND
        task.completed = 1);
    
    
    /*
       Same as collection tasksRequired = new collection
    */
    CREATE TABLE #RequiredTasks
    (
       taskID BIGINT
    );
    
    CREATE TABLE #RetrivedRequiredTasks
    (
       taskID BIGINT,
       compound BIT
    );
    
    INSERT INTO #RetrivedRequiredTasks
       (SELECT
        task.taskId,
        task.compound
        FROM
        usertTaskAssignment NATURAL JOIN
        assignmentRequiredTask NATURAL JOIN
        task
        WHERE
        userCompletedTask.userId = @userID);
    
    INSERT INTO #RequiredTasks
       (SELECT taskID FROM #RetrivedRequiredTasks WHERE compound = 0);
    
    INSERT INTO #RequiredTasks
       (SELECT
        compoundTask.childID
        FROM
        #RetrivedRequiredTasks INNER JOIN
        compoundTask on
        (
            #RetrivedRequiredTasks.compound = true  AND
            #RetrivedRequiredTasks.taskId = compoundTask.parentID
        ));
    
    DECLARE @count INT
    
    SELECT @count = Count(*)
    FROM #RequiredTasks
    WHERE taskId NOT IN (
        SELECT taskID FROM #TaskCompleted);
    
    IF @count = 0 THEN
       SELECT 1 /* All required tasks completed */
    ELSE
       SELECT 0 /* vice-versa */
    

    结束

    【讨论】:

      【解决方案3】:

      如果您只有一个级别的任务,那么更好的模型可能是拥有一个任务表和一个子任务表。对于非复合任务,他们只会有一个子任务而不是多个子任务。您可以在自己的模型中执行此操作,只需为 parentid = childid 的所有非复合任务添加行。

      除此之外,以下代码应为您提供用户所需的所有未完成任务的列表:

      SELECT
          COALESCE(CT.child_id, T.task_id)
      FROM
          User_Task_Assignments UTA
      INNER JOIN Assignment_Required_Tasks ART ON
          ART.assignment_id = UTA.assignment_id
      INNER JOIN Tasks T ON
          T.task_id = ART.task_id
      LEFT OUTER JOIN Compound_Tasks CT ON
          CT.parent_task_id = T.task_id AND
          T.compound = 1
      LEFT OUTER JOIN User_Completed_Tasks UCT ON
          UCT.user_id = @user_id AND
          UCT.task_id = COALESCE(CT.child_id, T.task_id)
      WHERE
          UTA.user_id = @user_id AND
          UCT.user_id IS NULL
      

      【讨论】:

      • 不幸的是,我不能假设子任务本身可能是复合任务。既然您提到它,这将涉及递归练习,直到我确定我拥有所有适当的任务。这可能会进一步复杂化。
      猜你喜欢
      • 2010-11-12
      • 2014-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-11
      • 2013-06-10
      • 1970-01-01
      相关资源
      最近更新 更多