One way is to change the structure of the table and add something like a CopiedFromTaskID column, which is usually NULL.
Each month, you copy all rows for each task that is not complete, and when you insert these new rows, you also update the CopiedFromTaskID column to be the identifier of the task from which each row was copied. This allows you to bind a new line to the line from which it was copied.
INSERT INTO tblTask (TaskDescription, TaskStatusID, ParentID, CopiedFromTaskID) SELECT TaskDescription, TaskStatusID, ParentID, TaskID FROM tblTask WHERE TaskStatusID <> Complete
Then you run SQL to change the ParentId of these newly inserted rows. Since you have a CopiedFromTaskID, you can use this to update the ParentID to reflect the new value, as in this SQL:
UPDATE tblTask SET tblTask.ParentID = InlineTable.NewTaskID FROM tblTask INNER JOIN ( SELECT TaskID AS NewTaskID, CopiedFromTaskID AS OldTaskID FROM tblTask WHERE CopiedFromTaskID IS NOT NULL ) AS InlineTable ON tblTask.TaskID = InlineTable.OldTaskID WHERE tblTask.CopiedFromTaskID IS NOT NULL
Finally, you refresh the table again to make all the CopiedFromTaskID NULL values ββso that it is ready for the next run:
UPDATE tblTask SET CopiedFromTaskID = NULL WHERE CopiedFromTaskID IS NOT NULL
You want to complete all these steps inside a transaction in a stored procedure, but it does what you want, without cursors / loops and inside the database. Obviously, you would need to drop the SQL statement to "close" all the original tasks.
Shawn source share