复杂的MySQL存储过程

时间:2009-03-24 14:49:28

标签: sql mysql stored-procedures

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

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

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,检查是否有合适的compoundTasks。

伪代码是这样的:

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条目本身可能是复合任务。因此,您需要进行递归式钻取以获取需要完成的所有非复合任务的列表。我扩展了上面的例子来举例说明这一点。

3 个答案:

答案 0 :(得分: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

对于指定的taskID,已完成从此返回的@user的任何内容。

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

答案 1 :(得分:0)

我是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 */

END

答案 2 :(得分:0)

如果你只有一个级别的任务,那么更好的模型可能是拥有一个任务表和一个子任务表。对于非复合任务,他们只需要在它们下面有一个子任务而不是倍数。您可以在自己的模型中执行此操作,只需为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