我需要创建一个半复杂的存储过程。我不是这里的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条目本身可能是复合任务。因此,您需要进行递归式钻取以获取需要完成的所有非复合任务的列表。我扩展了上面的例子来举例说明这一点。
答案 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以来已经很长时间了 - 所以这甚至可能都行不通。另外,如果您可以合并compoundTask
和task
表,则不需要第二个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