复杂的存储过程

时间:2014-03-19 08:48:19

标签: sql stored-procedures join sql-server-2012 subquery

我是sql的新手,我尝试创建一个相当复杂的存储过程,该过程将由使用Visual Studio商业智能中的sql报告服务创建的报告使用。

我有一个主要项目'通过使用链接表链接各种其他表格的表格(我的存储过程中感兴趣的是'状态','设施'分支') 。该应用程序的作用是研究人员提交项目并通过以下批准过程:

  

专业健康经理>分支头>健康管理团队

为了促进此审批流程,另一位团队成员开发了一个自定义工作流程解决方案,该解决方案利用了' WorkflowHistory'表。放入此表(以及其他)的是ProjectId,WorkflowStep和执行工作流程步骤的日期以及执行该步骤的人员发布的评论。我在报告中想要的是:

我在报告中想要的信息如下:

Mars report fields

' ProjectId'和'标题'来自'项目'表。 '设施''分支'和'状态'链接到'项目'的表格中的com。 '收到'需要是为项目执行初始工作流程步骤的日期。结论'需要是为项目执行最终工作流程步骤的日期。评论'需要是留给最后工作流程步骤的评论。

因此,报告中每一行的数据来自以下地方:

  • 来自' Project'
  • 的一行
  • 来自' Branch'
  • 的一行
  • 来自'状态'
  • 的一行
  • 来自' Facility'的多行(下面我的运行尝试我只从子查询返回第一个设施,但我希望所有设施都分配给项目,逗号空间分隔)
  • 来自' WorkflowHistory'
  • 的两个不同的行

用户传递以下参数以过滤报告:

  • 从日期开始 - 获取在特定日期之后收到的所有报告(这将是' ActionedOn'用于' WorkflowHistory'中的第一个工作流程步骤)
  • 到目前为止 - 获取在特定日期之前收到的所有报告(这将是' ActionedOn'用于' WorkflowHistory'中的最终工作流程步骤)
  • 状态 - 过滤具有特定状态的项目
  • 分支 - 过滤分配给特定分支的项目

Mars Report parameters

我尝试使用以下存储过程完成所有这些操作。这是我的继续尝试,我一直在努力。我仍然遇到的问题包括:

  • 日期范围过滤不起作用
  • 我只能返回子查询中的第一个工具 (以下是注释掉整个WHERE部分时仍然存在的问题)
  • 项目的最终工作流程步骤可能介于2到5之间,具体取决于是否已获批准以及何时被拒绝。我需要弄清楚如何获得结论'日期以及此步骤中留下的评论。
  • 我通过了状态'在参数中。我需要弄清楚如何通过一种状态或所有状态提交(实际上并非所有状态,但最终的3是'已批准','拒绝'和'总结&#39)。分支相同。

编辑:现在是5小时之后,我已经更新了下面的存储过程。我通过使用临时表变量

对大部分问题进行了排序
CREATE PROCEDURE [dbo].[stp_CityHealthResearchRequestsReport]
@FromDate DATETIME,
@ToDate DATETIME,
@StatusId int,
@BranchId int,
@Count INT OUTPUT
AS
BEGIN
DECLARE @TempTable TABLE
(
    ProjectId INT,
    Recieved DATETIME,
    Concluded DATETIME,
    Comment VARCHAR(8000)
)

IF @StatusId <> 0 AND @BranchId <> 0
BEGIN

    INSERT INTO @TempTable (ProjectId, Recieved, Concluded, Comment)
    SELECT DISTINCT
        p.ProjectId,
        (SELECT TOP 1 wf.ActionedOn
         FROM WorkflowHistory wf
         WHERE wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 1
         ORDER BY wf.WorkflowHistoryId DESC) AS Recieved,
        (SELECT TOP 1 wf.ActionedOn
         FROM WorkflowHistory wf
         WHERE wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 4
         OR wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 5
         ORDER BY wf.WorkflowHistoryId DESC) AS Concluded,
        (SELECT TOP 1 wf.Comment
         FROM WorkflowHistory wf
         WHERE wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 4
         OR wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 5
         ORDER BY wf.WorkflowHistoryId DESC) AS Comment
    FROM
        Project p
        JOIN WorkflowHistory w ON p.ProjectId = w.ProjectId
        JOIN ProjectBranch pb ON pb.ProjectId = p.ProjectId
    WHERE
        p.ProjectId = w.ProjectId
        AND p.StatusId = @StatusId
        AND pb.BranchId = @BranchId
        AND w.WorkflowStep = 1
        AND (w.ActionedOn BETWEEN @FromDate AND @ToDate)

    SELECT DISTINCT
        p.ProjectId,
        p.Title,
        STUFF (
               (SELECT ', ' + f.Name
                FROM dbo.Facility f
                LEFT JOIN dbo.ProjectFacility pf ON f.FacilityId = pf.FacilityId
                WHERE pf.ProjectId = p.ProjectId
                FOR XML PATH (''))
                , 1, 1, '') AS Facilities,
        tt.Recieved,
        tt.Concluded,
        b.BranchName,
        st.Description AS StatusText,
        tt.Comment,
        tt.Concluded - tt.Recieved AS Turnaround
    FROM
        dbo.Project p
        INNER JOIN @TempTable tt ON p.ProjectId = tt.ProjectId
        LEFT JOIN dbo.ProjectBranch pb ON p.ProjectId = pb.ProjectId
        LEFT JOIN dbo.Branch b ON pb.BranchId = b.BranchId
        LEFT JOIN dbo.Status st ON p.StatusId = st.StatusId
    WHERE
        p.StatusId = @StatusId
        AND b.BranchId = @BranchId
    SET @Count = @@ROWCOUNT
END

IF @StatusId <> 0 AND @BranchId = 0
BEGIN

    INSERT INTO @TempTable (ProjectId, Recieved, Concluded, Comment)
    SELECT DISTINCT
        p.ProjectId,
        (SELECT TOP 1 wf.ActionedOn
         FROM WorkflowHistory wf
         WHERE wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 1
         ORDER BY wf.WorkflowHistoryId DESC) AS Recieved,
        (SELECT TOP 1 wf.ActionedOn
         FROM WorkflowHistory wf
         WHERE wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 4
         OR wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 5
         ORDER BY wf.WorkflowHistoryId DESC) AS Concluded,
        (SELECT TOP 1 wf.Comment
         FROM WorkflowHistory wf
         WHERE wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 4
         OR wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 5
         ORDER BY wf.WorkflowHistoryId DESC) AS Comment
    FROM
        Project p
        JOIN WorkflowHistory w ON p.ProjectId = w.ProjectId
        JOIN ProjectBranch pb ON pb.ProjectId = p.ProjectId
    WHERE
        p.ProjectId = w.ProjectId
        AND p.StatusId = @StatusId
        AND w.WorkflowStep = 1
        AND (w.ActionedOn BETWEEN @FromDate AND @ToDate)

    SELECT DISTINCT
        p.ProjectId,
        p.Title,
        STUFF (
               (SELECT ', ' + f.Name
                FROM dbo.Facility f
                LEFT JOIN dbo.ProjectFacility pf ON f.FacilityId = pf.FacilityId
                WHERE pf.ProjectId = p.ProjectId
                FOR XML PATH (''))
                , 1, 1, '') AS Facilities,
        tt.Recieved,
        tt.Concluded,
        b.BranchName,
        st.Description AS StatusText,
        tt.Comment,
        tt.Concluded - tt.Recieved AS Turnaround
    FROM
        dbo.Project p
        INNER JOIN @TempTable tt ON p.ProjectId = tt.ProjectId
        LEFT JOIN dbo.ProjectBranch pb ON p.ProjectId = pb.ProjectId
        LEFT JOIN dbo.Branch b ON pb.BranchId = b.BranchId
        LEFT JOIN dbo.Status st ON p.StatusId = st.StatusId
    WHERE
        p.StatusId = @StatusId
    SET @Count = @@ROWCOUNT
END

IF @StatusId = 0 AND @BranchId <> 0
BEGIN

    INSERT INTO @TempTable (ProjectId, Recieved, Concluded, Comment)
    SELECT DISTINCT
        p.ProjectId,
        (SELECT TOP 1 wf.ActionedOn
         FROM WorkflowHistory wf
         WHERE wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 1
         ORDER BY wf.WorkflowHistoryId DESC) AS Recieved,
        (SELECT TOP 1 wf.ActionedOn
         FROM WorkflowHistory wf
         WHERE wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 4
         OR wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 5
         ORDER BY wf.WorkflowHistoryId DESC) AS Concluded,
        (SELECT TOP 1 wf.Comment
         FROM WorkflowHistory wf
         WHERE wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 4
         OR wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 5
         ORDER BY wf.WorkflowHistoryId DESC) AS Comment
    FROM
        Project p
        JOIN WorkflowHistory w ON p.ProjectId = w.ProjectId
        JOIN ProjectBranch pb ON pb.ProjectId = p.ProjectId
    WHERE
        p.ProjectId = w.ProjectId
        AND p.StatusId = 5
        AND pb.BranchId = @BranchId
        AND w.WorkflowStep = 1
        AND (w.ActionedOn BETWEEN @FromDate AND @ToDate)
        OR p.ProjectId = w.ProjectId
        AND p.StatusId = 6
        AND pb.BranchId = @BranchId
        AND w.WorkflowStep = 1
        AND (w.ActionedOn BETWEEN @FromDate AND @ToDate)
        OR p.ProjectId = w.ProjectId
        AND p.StatusId = 7
        AND pb.BranchId = @BranchId
        AND w.WorkflowStep = 1
        AND (w.ActionedOn BETWEEN @FromDate AND @ToDate)

    SELECT DISTINCT
        p.ProjectId,
        p.Title,
        STUFF (
               (SELECT ', ' + f.Name
                FROM dbo.Facility f
                LEFT JOIN dbo.ProjectFacility pf ON f.FacilityId = pf.FacilityId
                WHERE pf.ProjectId = p.ProjectId
                FOR XML PATH (''))
                , 1, 1, '') AS Facilities,
        tt.Recieved,
        tt.Concluded,
        b.BranchName,
        st.Description AS StatusText,
        tt.Comment,
        tt.Concluded - tt.Recieved AS Turnaround
    FROM
        dbo.Project p
        INNER JOIN @TempTable tt ON p.ProjectId = tt.ProjectId
        LEFT JOIN dbo.ProjectBranch pb ON p.ProjectId = pb.ProjectId
        LEFT JOIN dbo.Branch b ON pb.BranchId = b.BranchId
        LEFT JOIN dbo.Status st ON p.StatusId = st.StatusId
    WHERE
        p.StatusId = 5
        AND pb.BranchId = @BranchId
        OR p.StatusId = 6
        AND pb.BranchId = @BranchId
        OR p.StatusId = 7
        AND pb.BranchId = @BranchId
    SET @Count = @@ROWCOUNT
END

IF @StatusId = 0 AND @BranchId = 0
BEGIN

    INSERT INTO @TempTable (ProjectId, Recieved, Concluded, Comment)
    SELECT DISTINCT
        p.ProjectId,
        (SELECT TOP 1 wf.ActionedOn
         FROM WorkflowHistory wf
         WHERE wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 1
         ORDER BY wf.WorkflowHistoryId DESC) AS Recieved,
        (SELECT TOP 1 wf.ActionedOn
         FROM WorkflowHistory wf
         WHERE wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 4
         OR wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 5
         ORDER BY wf.WorkflowHistoryId DESC) AS Concluded,
        (SELECT TOP 1 wf.Comment
         FROM WorkflowHistory wf
         WHERE wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 4
         OR wf.ProjectId = p.ProjectId
         AND wf.WorkflowStep = 5
         ORDER BY wf.WorkflowHistoryId DESC) AS Comment
    FROM
        Project p
        JOIN WorkflowHistory w ON p.ProjectId = w.ProjectId
        JOIN ProjectBranch pb ON pb.ProjectId = p.ProjectId
    WHERE
        p.ProjectId = w.ProjectId
        AND p.StatusId = 5
        AND w.WorkflowStep = 1
        AND (w.ActionedOn BETWEEN @FromDate AND @ToDate)
        OR p.ProjectId = w.ProjectId
        AND p.StatusId = 6
        AND w.WorkflowStep = 1
        AND (w.ActionedOn BETWEEN @FromDate AND @ToDate)
        OR p.ProjectId = w.ProjectId
        AND p.StatusId = 7
        AND w.WorkflowStep = 1
        AND (w.ActionedOn BETWEEN @FromDate AND @ToDate)

    SELECT DISTINCT
        p.ProjectId,
        p.Title,
        STUFF (
               (SELECT ', ' + f.Name
                FROM dbo.Facility f
                LEFT JOIN dbo.ProjectFacility pf ON f.FacilityId = pf.FacilityId
                WHERE pf.ProjectId = p.ProjectId
                FOR XML PATH (''))
                , 1, 1, '') AS Facilities,
        tt.Recieved,
        tt.Concluded,
        b.BranchName,
        st.Description AS StatusText,
        tt.Comment,
        tt.Concluded - tt.Recieved AS Turnaround
    FROM
        dbo.Project p
        INNER JOIN @TempTable tt ON p.ProjectId = tt.ProjectId
        LEFT JOIN dbo.ProjectBranch pb ON p.ProjectId = pb.ProjectId
        LEFT JOIN dbo.Branch b ON pb.BranchId = b.BranchId
        LEFT JOIN dbo.Status st ON p.StatusId = st.StatusId
    WHERE
        p.StatusId = 5
        OR p.StatusId = 6
        OR p.StatusId = 7
    SET @Count = @@ROWCOUNT
END
END

1 个答案:

答案 0 :(得分:5)

您的代码几乎没有问题:

  • 您反复重复相同的代码。而是通过@StatusId和@BranchId进行分支,您可以在WHERE子句中处理它。
  • 您使用@StatusId = 0和@BranchId = 0表示缺失值,这应该用NULL表示。
  • 在使用标量相关子查询的地方,我会使用连接。

说够了,我相信这个查询可以做你想要的:

-- find out the final step for each project
WITH FinalStep AS
(
    SELECT ProjectId, MAX(WorkflowStep) as MaxWorkflowStep
    FROM WorkflowHistory
    WHERE WorkflowStep > 1
    GROUP BY ProjectId
)
SELECT
    p.ProjectId,
    p.Title,
    -- this is ugly consider creating a scalar function to encapsulate it
    STUFF (
           (SELECT ', ' + f.Name
            FROM dbo.Facility f
            LEFT JOIN dbo.ProjectFacility pf ON f.FacilityId = pf.FacilityId
            WHERE pf.ProjectId = p.ProjectId
            FOR XML PATH (''))
            , 1, 1, '') AS Facilities,
    wf1.ActionedOn AS Recieved,
    wf2.ActionedOn AS Concluded,
    b.BranchName,
    st.Description AS StatusText,
    wf2.Comment,
    wf2.ActionedOn - w1.ActionedOn AS Turnaround
FROM
    Project p
    INNER JOIN WorkflowHistory wf1 
        ON p.ProjectId = wf1.ProjectId 
        AND wf1.WorkflowStep = 1
    LEFT JOIN FinalStep fs 
        ON fs.ProjectId = p.ProjectId
    LEFT JOIN WorkflowHistory wf2 
        ON p.ProjectId = wf2.ProjectId 
        AND wf2.WorkflowStep = fs.MaxWorkflowStep
    INNER JOIN ProjectBranch pb 
        ON pb.ProjectId = p.ProjectId
    LEFT JOIN dbo.Branch b 
        ON pb.BranchId = b.BranchId
    LEFT JOIN dbo.Status st 
        ON p.StatusId = st.StatusId
WHERE
    p.ProjectId = w.ProjectId
    -- IF @StatusId = 0 THEN p.StatusId = 5 or 6 or 7 ELSE p.StatusId = @StatusId
    AND ((@StatusId = 0 AND p.StatusId IN (5,6,7)) OR p.StatusId = @StatusId)
    -- IF @BranchId = 0 THEN no filter ELSE pb.BranchId = @BranchId
    AND (@BranchId = 0 OR pb.BranchId = @BranchId)
    AND (wf1.ActionedOn BETWEEN @FromDate AND @ToDate)

我不知道为什么日期过滤器不起作用,对我来说没问题。我没有检查设施连接,但你应该能够很容易谷歌这个东西。我建议你将代码提取到一个函数中。然后,您可以单独测试它,查询看起来会更好。