Index: trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/DimJobDao.cs
===================================================================
--- trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/DimJobDao.cs	(revision 17184)
+++ trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/HiveStatistics/DimJobDao.cs	(revision 17574)
@@ -44,4 +44,8 @@
     }
 
+    public void UpdateExistingDimJobs() {
+      DataContext.ExecuteCommand(UpdateExistingDimJobsQuery);
+    }
+
     #region Compiled queries
     private static readonly Func<DataContext, Guid, DimJob> GetByIdQuery =
@@ -51,4 +55,40 @@
          select dimJob).SingleOrDefault());
     #endregion
+
+    #region String queries
+    private const string UpdateExistingDimJobsQuery = @"
+UPDATE u
+SET
+  u.JobName = case when x.JobId is null then u.JobName else x.JobName end,
+  u.TotalTasks = x.TotalTasks,
+  u.CompletedTasks = x.CompletedTasks,
+  u.DateCompleted =
+    case when x.totaltasks = x.CompletedTasks
+      then (case when x.JobId is null and x.DateCompleted is null then GETDATE() else x.DateCompleted end)
+    else u.DateCompleted
+  end,
+  u.ProjectId = case when x.JobId is null then u.ProjectId else x.ProjectId end
+FROM [statistics].dimjob u
+JOIN (
+	SELECT
+	  dj.JobId as DimJobId,
+	  j.JobId as JobId,
+	  j.Name as JobName,
+	  COUNT(*) as TotalTasks,
+	  SUM(
+	  CASE
+		WHEN TaskState in ('Finished', 'Aborted', 'Failed') then 1
+		ELSE 0
+	  END) as CompletedTasks,
+	  MAX(EndTime) as DateCompleted,
+	  dp.ProjectId as ProjectId
+	from [statistics].DimJob dj
+	join [statistics].FactTask ft on dj.JobId = ft.JobId
+	left join Job j on j.JobId = dj.JobId
+	left join [statistics].DimProject dp on j.ProjectId = dp.ProjectId 
+	where dj.DateCompleted is null and dp.DateExpired is null
+	group by dj.JobId, j.JobId, j.Name, dp.ProjectId 
+) as x on u.JobId = x.DimJobId";
+    #endregion
   }
 }
Index: trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/JobDao.cs
===================================================================
--- trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/JobDao.cs	(revision 17184)
+++ trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/JobDao.cs	(revision 17574)
@@ -37,4 +37,8 @@
     }
 
+    public int DeleteByState(JobState state, int batchSize) {
+      return DataContext.ExecuteCommand(DeleteTopNByStateQueryString, batchSize, Enum.GetName(typeof(JobState), state));
+    }
+
     public IEnumerable<Job> GetByProjectId(Guid id) {
       return GetByProjectIdQuery(DataContext, id);
@@ -43,5 +47,5 @@
     public IEnumerable<Job> GetByProjectIds(IEnumerable<Guid> projectIds) {
       string paramProjectIds = string.Join(",", projectIds.ToList().Select(x => string.Format("'{0}'", x)));
-      if(!string.IsNullOrWhiteSpace(paramProjectIds)) {
+      if (!string.IsNullOrWhiteSpace(paramProjectIds)) {
         string queryString = string.Format(GetByProjectIdsQueryString, paramProjectIds);
         return DataContext.ExecuteQuery<Job>(queryString);
@@ -51,9 +55,9 @@
 
     public IEnumerable<Job> GetByState(JobState state) {
-      return GetByStateQuery(DataContext, state);
+      return GetByStateQuery(DataContext, state.ToString());
     }
 
     public IEnumerable<Guid> GetJobIdsByState(JobState state) {
-      return GetJobIdsByStateQuery(DataContext, state);
+      return GetJobIdsByStateQuery(DataContext, state.ToString());
     }
 
@@ -74,13 +78,13 @@
          where job.ProjectId == projectId
          select job));
-    private static readonly Func<DataContext, JobState, IEnumerable<Job>> GetByStateQuery =
-      CompiledQuery.Compile((DataContext db, JobState jobState) =>
+    private static readonly Func<DataContext, string, IEnumerable<Job>> GetByStateQuery =
+      CompiledQuery.Compile((DataContext db, string jobState) =>
         (from job in db.GetTable<Job>()
-         where job.State == jobState
+         where job.State.ToString() == jobState
          select job));
-    private static readonly Func<DataContext, JobState, IEnumerable<Guid>> GetJobIdsByStateQuery =
-      CompiledQuery.Compile((DataContext db, JobState jobState) =>
+    private static readonly Func<DataContext, string, IEnumerable<Guid>> GetJobIdsByStateQuery =
+      CompiledQuery.Compile((DataContext db, string jobState) =>
         (from job in db.GetTable<Job>()
-         where job.State == jobState
+         where job.State.ToString() == jobState
          select job.JobId));
     private static readonly Func<DataContext, IEnumerable<Job>> GetJobsReadyForDeletionQuery =
@@ -101,4 +105,9 @@
       WHERE JobState = {0}
     ";
+    private const string DeleteTopNByStateQueryString = @"
+      DELETE TOP ({0})
+      FROM [Job]
+      WHERE JobState = {1}
+    ";
     private const string GetStatisticsPendingJobs = @"
       SELECT DISTINCT j.*
Index: trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/StateLogDao.cs
===================================================================
--- trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/StateLogDao.cs	(revision 17184)
+++ trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/StateLogDao.cs	(revision 17574)
@@ -40,4 +40,8 @@
     }
 
+    public int DeleteObsolete(int batchSize) {
+      return DataContext.ExecuteCommand(DeleteObsoleteQueryString, batchSize);
+    }
+
     #region Compiled queries
     private static readonly Func<DataContext, Guid, StateLog> GetByIdQuery =
@@ -54,4 +58,14 @@
          select stateLog).First(x => x.SlaveId != null));
     #endregion
+
+    #region String queries
+    private const string DeleteObsoleteQueryString = @"
+delete top ({0}) s
+from statelog s
+  join task t on t.taskid = s.taskid
+  join job j on j.jobid = t.jobid
+where j.jobstate = 'deletionpending'
+    ";
+    #endregion
   }
 }
Index: trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/TaskDao.cs
===================================================================
--- trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/TaskDao.cs	(revision 17184)
+++ trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/TaskDao.cs	(revision 17574)
@@ -50,8 +50,8 @@
       //Because there is at the moment no case where this makes sense (there don't exist parent tasks which need to be calculated), 
       //we skip this step because it's wasted runtime
-      return DataContext.ExecuteQuery<TaskPriorityInfo>(GetWaitingTasksQueryString, 
-        slave.ResourceId, 
-        Enum.GetName(typeof(TaskState), TaskState.Waiting), 
-        slave.FreeCores, 
+      return DataContext.ExecuteQuery<TaskPriorityInfo>(GetWaitingTasksQueryString,
+        slave.ResourceId,
+        Enum.GetName(typeof(TaskState), TaskState.Waiting),
+        slave.FreeCores,
         slave.FreeMemory).ToList();
     }
@@ -65,14 +65,14 @@
     /// <returns></returns>
     public IEnumerable<Task> GetParentTasks(IEnumerable<Guid> resourceIds, int count, bool finished) {
-    var query = from t in Table
-                where t.State == TaskState.Waiting
-                    && t.IsParentTask
-                    && t.Job.AssignedJobResources.All(x => resourceIds.ToList().Contains(x.ResourceId))
-                    && t.FinishWhenChildJobsFinished == finished
-                    && t.ChildJobs.Any()
-                    && t.ChildJobs.All(x =>
-                      x.State == TaskState.Finished
-                      || x.State == TaskState.Aborted
-                      || x.State == TaskState.Failed)
+      var query = from t in Table
+                  where t.State == TaskState.Waiting
+                      && t.IsParentTask
+                      && t.Job.AssignedJobResources.All(x => resourceIds.ToList().Contains(x.ResourceId))
+                      && t.FinishWhenChildJobsFinished == finished
+                      && t.ChildJobs.Any()
+                      && t.ChildJobs.All(x =>
+                        x.State == TaskState.Finished
+                        || x.State == TaskState.Aborted
+                        || x.State == TaskState.Failed)
                   orderby t.Priority descending
                   select t;
@@ -82,4 +82,8 @@
     public void UpdateExecutionTime(Guid taskId, double executionTime) {
       DataContext.ExecuteCommand(UpdateExecutionTimeQuery, executionTime, DateTime.Now, taskId);
+    }
+
+    public int DeleteObsolete(int batchSize) {
+      return DataContext.ExecuteCommand(DeleteObsoleteQueryString, batchSize);
     }
 
@@ -129,4 +133,12 @@
        WHERE TaskId = {2}
     ";
+
+    private const string DeleteObsoleteQueryString = @"
+delete top ({0}) t1
+from task t1
+  left join task t2 on t1.taskid = t2.parenttaskid
+  join job j on j.jobid = t1.jobid
+where j.jobstate = 'deletionpending' and t2.taskid is null
+";
     #endregion
   }
Index: trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/TaskDataDao.cs
===================================================================
--- trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/TaskDataDao.cs	(revision 17184)
+++ trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Daos/TaskDataDao.cs	(revision 17574)
@@ -32,4 +32,8 @@
     }
 
+    public int DeleteObsolete(int batchSize) {
+      return DataContext.ExecuteCommand(DeleteObsoleteQueryString, batchSize);
+    }
+
     #region Compiled queries
     private static readonly Func<DataContext, Guid, TaskData> GetByIdQuery =
@@ -39,4 +43,14 @@
          select taskData).SingleOrDefault());
     #endregion
+
+    #region String queries
+    private const string DeleteObsoleteQueryString = @"
+delete top ({0}) td
+from taskdata td
+  join task t on t.taskid = td.taskid
+  join job j on j.jobid = t.jobid
+where j.jobstate = 'deletionpending'
+    ";
+    #endregion
   }
 }
Index: trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Interfaces/IPersistenceManager.cs
===================================================================
--- trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Interfaces/IPersistenceManager.cs	(revision 17184)
+++ trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Interfaces/IPersistenceManager.cs	(revision 17574)
@@ -64,5 +64,7 @@
     #region Transaction management
     void UseTransaction(Action call, bool repeatableRead = false, bool longRunning = false);
+    void UseTransactionAndSubmit(Action call, bool repeatableRead = false, bool longRunning = false);
     T UseTransaction<T>(Func<T> call, bool repeatableRead = false, bool longRunning = false);
+    T UseTransactionAndSubmit<T>(Func<T> call, bool repeatableRead = false, bool longRunning = false);
     #endregion
 
Index: trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Manager/PersistenceManager.cs
===================================================================
--- trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Manager/PersistenceManager.cs	(revision 17184)
+++ trunk/HeuristicLab.Services.Hive.DataAccess/3.3/Manager/PersistenceManager.cs	(revision 17574)
@@ -184,8 +184,9 @@
     #region Transaction management
     public void UseTransaction(Action call, bool repeatableRead = false, bool longRunning = false) {
-      UseTransaction<object>(() => {
-        call();
-        return null;
-      });
+      UseTransaction<object>(() => { call(); return null; }, repeatableRead, longRunning);
+    }
+
+    public void UseTransactionAndSubmit(Action call, bool repeatableRead = false, bool longRunning = false) {
+      UseTransaction(() => { call(); SubmitChanges(); }, repeatableRead, longRunning);
     }
 
@@ -211,4 +212,8 @@
     }
 
+    public T UseTransactionAndSubmit<T>(Func<T> call, bool repeatableRead = false, bool longRunning = false) {
+      return UseTransaction(() => { var res = call(); SubmitChanges(); return res; }, repeatableRead, longRunning);
+    }
+
     private static TransactionScope CreateTransaction(bool repeatableRead, bool longRunning) {
       var options = new TransactionOptions {
Index: trunk/HeuristicLab.Services.Hive.DataAccess/3.3/SQL Scripts/Prepare Hive Database.sql
===================================================================
--- trunk/HeuristicLab.Services.Hive.DataAccess/3.3/SQL Scripts/Prepare Hive Database.sql	(revision 17184)
+++ trunk/HeuristicLab.Services.Hive.DataAccess/3.3/SQL Scripts/Prepare Hive Database.sql	(revision 17574)
@@ -141,60 +141,2 @@
 INCLUDE ([TaskId],[ExecutionTimeMs],[LastHeartbeat],[ParentTaskId],[Priority],[Command],[JobId])
 GO
-
-
-
--- OBSOLETE - DO NOT PERFORM (start)
-/****** Object:  Trigger [dbo].[tr_JobDeleteCascade]    Script Date: 04/19/2011 16:31:53 ******/
-SET ANSI_NULLS ON
-GO
-SET QUOTED_IDENTIFIER ON
-GO
--- =============================================
--- Author:		cneumuel
--- Create date: 19.04.2011
--- Description:	(1) Writes the execution times of deleted jobs into DeletedJobStats to ensure correct statistics
---				(2) Deletes all associated jobs. This cannot be done with cascading delete, 
---              because the job table defines a INSTEAD OF DELETE trigger itself, which
---              is not compatible with cascading deletes.
--- =============================================
-CREATE TRIGGER [dbo].[tr_JobDeleteCascade] ON [dbo].[Job] INSTEAD OF DELETE AS 
-BEGIN
-    DELETE Task FROM deleted, Task WHERE deleted.JobId = Task.JobId
-    DELETE Job FROM deleted, Job WHERE deleted.JobId = Job.JobId
-END
-GO
-
--- =============================================
--- Author:		cneumuel
--- Create date: 11.11.2010
--- Description:	Recursively deletes all child-jobs of a job when it is deleted. (Source: http://devio.wordpress.com/2008/05/23/recursive-delete-in-sql-server/)
--- =============================================DeletedJobStatistics
-CREATE TRIGGER [dbo].[tr_TaskDeleteCascade] ON [dbo].[Task] INSTEAD OF DELETE AS 
-BEGIN
-    -- recursively delete jobs
-    CREATE TABLE #Table( 
-        TaskId uniqueidentifier 
-    )
-    INSERT INTO #Table (TaskId)
-    SELECT TaskId FROM deleted
-    
-    DECLARE @c INT
-    SET @c = 0
-    
-    WHILE @c <> (SELECT COUNT(TaskId) FROM #Table) BEGIN
-        SELECT @c = COUNT(TaskId) FROM #Table
-        
-        INSERT INTO #Table (TaskId)
-            SELECT Task.TaskId
-            FROM Task
-            LEFT OUTER JOIN #Table ON Task.TaskId = #Table.TaskId
-            WHERE Task.ParentTaskId IN (SELECT TaskId FROM #Table)
-                AND #Table.TaskId IS NULL
-    END
-    
-    DELETE TaskData FROM TaskData INNER JOIN #Table ON TaskData.TaskId = #Table.TaskId
-    DELETE Task FROM Task INNER JOIN #Table ON Task.TaskId = #Table.TaskId
-END
-GO
--- OBSOLETE (end)
-
