复杂的SQL查询我无法弄清楚

时间:2014-07-01 17:45:56

标签: mysql sql

我正在为开源工具Validation Manager创建报告。我设法在完成大量工作后提出了所有要求的报告。我正在尝试创建一个报告来列出未覆盖的报告,但是使用not in会使查询进入永无止境的处理状态。

这是DD图:

DB diagram

所涉及的表格位于图表的右下方。可以获得样本数据here。获得未覆盖的要求的正确方法是什么?

仅供参考:未披露的要求是那些没有相关步骤和/或其子女要求的要求。

主要问题是理论上存在无限多级别的需求关系,而SQL我只能处理2个级别。试图找出一种尽可能深入的方式。

作为参考,请参阅以下有关要求的查询(与我需要的相反):

select
    c.covered, t.total
from
    (SELECT DISTINCT
        count(distinct unique_id) as covered
    FROM
        requirement
    WHERE
        requirement.id IN (select
                requirement.id
            from
                `requirement` requirement
            INNER JOIN `step_has_requirement` step_has_requirement ON requirement.`id` = step_has_requirement.`requirement_id`
            INNER JOIN `step` step ON step_has_requirement.`step_id` = step.`id`
                AND step.`test_case_id` = step_has_requirement.`step_test_case_id`
                AND step.`test_case_test_id` = step_has_requirement.`step_test_case_test_id`
            INNER JOIN `test_case` test_case ON step.`test_case_id` = test_case.`id`
                AND test_case.`test_id` = step.`test_case_test_id`
            INNER JOIN `test` test ON test_case.`test_id` = test.`id`
            INNER JOIN `test_plan_has_test` test_plan_has_test ON test.`id` = test_plan_has_test.`test_id`
            INNER JOIN `test_plan` test_plan ON test_plan_has_test.`test_plan_id` = test_plan.`id`
                AND test_plan.`test_project_id` = test_plan_has_test.`test_plan_test_project_id`
            INNER JOIN `test_project` test_project ON test_plan.`test_project_id` = test_project.`id`
            INNER JOIN `requirement_status` requirement_status ON requirement.`requirement_status_id` = requirement_status.`id`
            INNER JOIN `requirement_spec_node` requirement_spec_node ON requirement.`requirement_spec_node_id` = requirement_spec_node.`id`
                AND requirement_spec_node.`requirement_spec_project_id` = requirement.`requirement_spec_node_requirement_spec_project_id`
                AND requirement_spec_node.`requirement_spec_spec_level_id` = requirement.`requirement_spec_node_requirement_spec_spec_level_id`
                AND requirement_spec_node.`requirement_spec_id` = requirement.`requirement_spec_node_requirement_spec_id`
            INNER JOIN `requirement_spec` requirement_spec ON requirement_spec_node.`requirement_spec_id` = requirement_spec.`id`
                AND requirement_spec.`project_id` = requirement_spec_node.`requirement_spec_project_id`
                AND requirement_spec.`spec_level_id` = requirement_spec_node.`requirement_spec_spec_level_id`
            INNER JOIN `project` project ON requirement_spec.`project_id` = project.`id`
            WHERE
                requirement_status.status = 'general.approved'
                    and (project.id = $P{target_project_id}
                    or project.parent_project_id = $P{target_project_id}))
        or requirement.id IN (select parent_requirement_id from requirement_has_requirement where parent_requirement_id = requirement.id
and requirement_id in (select
                requirement.id
            from
                `requirement` requirement
            INNER JOIN `step_has_requirement` step_has_requirement ON requirement.`id` = step_has_requirement.`requirement_id`
            INNER JOIN `step` step ON step_has_requirement.`step_id` = step.`id`
                AND step.`test_case_id` = step_has_requirement.`step_test_case_id`
                AND step.`test_case_test_id` = step_has_requirement.`step_test_case_test_id`
            INNER JOIN `test_case` test_case ON step.`test_case_id` = test_case.`id`
                AND test_case.`test_id` = step.`test_case_test_id`
            INNER JOIN `test` test ON test_case.`test_id` = test.`id`
            INNER JOIN `test_plan_has_test` test_plan_has_test ON test.`id` = test_plan_has_test.`test_id`
            INNER JOIN `test_plan` test_plan ON test_plan_has_test.`test_plan_id` = test_plan.`id`
                AND test_plan.`test_project_id` = test_plan_has_test.`test_plan_test_project_id`
            INNER JOIN `test_project` test_project ON test_plan.`test_project_id` = test_project.`id`
            INNER JOIN `requirement_status` requirement_status ON requirement.`requirement_status_id` = requirement_status.`id`
            INNER JOIN `requirement_spec_node` requirement_spec_node ON requirement.`requirement_spec_node_id` = requirement_spec_node.`id`
                AND requirement_spec_node.`requirement_spec_project_id` = requirement.`requirement_spec_node_requirement_spec_project_id`
                AND requirement_spec_node.`requirement_spec_spec_level_id` = requirement.`requirement_spec_node_requirement_spec_spec_level_id`
                AND requirement_spec_node.`requirement_spec_id` = requirement.`requirement_spec_node_requirement_spec_id`
            INNER JOIN `requirement_spec` requirement_spec ON requirement_spec_node.`requirement_spec_id` = requirement_spec.`id`
                AND requirement_spec.`project_id` = requirement_spec_node.`requirement_spec_project_id`
                AND requirement_spec.`spec_level_id` = requirement_spec_node.`requirement_spec_spec_level_id`
            INNER JOIN `project` project ON requirement_spec.`project_id` = project.`id`
            WHERE
                requirement_status.status = 'general.approved'
                    and (project.id = $P{target_project_id}
                    or project.parent_project_id = $P{target_project_id})))
    order by unique_id) c,
    (SELECT
        count(distinct unique_id) AS total
    FROM
        `requirement_status` requirement_status
    INNER JOIN `requirement` requirement ON requirement_status.`id` = requirement.`requirement_status_id`
    INNER JOIN `requirement_spec_node` requirement_spec_node ON requirement.`requirement_spec_node_id` = requirement_spec_node.`id`
        AND requirement_spec_node.`requirement_spec_id` = requirement.`requirement_spec_node_requirement_spec_id`
    INNER JOIN `requirement_spec` requirement_spec ON requirement_spec_node.`requirement_spec_id` = requirement_spec.`id`
        AND requirement_spec.`spec_level_id` = requirement_spec_node.`requirement_spec_spec_level_id`
        AND requirement_spec.`project_id` = requirement_spec_node.`requirement_spec_project_id`
    INNER JOIN `project` project ON requirement_spec.`project_id` = project.`id`
    INNER JOIN `spec_level` spec_level ON requirement_spec.`spec_level_id` = spec_level.`id`
    WHERE
        requirement_status.status = 'general.approved'
            and (project.id = $P{target_project_id}
            or project.parent_project_id = $P{target_project_id})) t

注意:SQL有一个关于我如何设法做相反的例子,得到了涵盖的要求。我想得到那些没有报道的。 SQl查询工作正常。

我正在寻找的内容与现在未涵盖的部分相同(上文)!

2 个答案:

答案 0 :(得分:1)

首先是

你的架构是你最大的敌人。我已将SQLFiddle与您的step表格相关联。这是一个非常令人印象深刻的表,但令人印象深刻的并不总是可以在SQL中使用。您可以很好地重构表并使它们标准化。有很多非常的情况下,将多个整数放入一个文本文件中并用逗号分隔它们是有意义的。如果您将要在该列中放入的唯一内容是由列分隔的整数,那么您将无法使您的SQL架构成为First Normal Form。虽然以这种方式设计您的架构似乎更容易,但只是在表面上才是您这样做的。实际上,你正在为自己创造一堆史诗般的比例 - 正如你现在可能正在发现的那样。由于未能满足First Normal Form,您无法利用SQL固有的任何关系权力。

修改这是一个相当大的架构。我对1NF的反应是text的{​​{1}}部分。我真的不知道它的目的是什么,所以很难肯定地说,但是当我在用逗号分隔的文本框中看到多个具有相同整数列的行时,它会竖起一个大红旗。这通常表明多个列已连接成一列。在检查了模式的其他部分之后,很明显其他部分已经规范化。它似乎不是您架构中不可或缺的一部分,所以,我的错误。话虽如此,这仍然是一个非常复杂的应用程序,具有大量的交叉引用表。同时将steprequirement_has_requirement作为表可能会有好处,但它也有严重的缺点。

我怀疑您是否需要以当前的方式区分步骤与需求。要求不只是另一步吗?我知道您需要不同的列,但这可以通过使用step_has_requirementrequirement_addendum表来解决,该表可以根据需要调用,也可以在需要时忽略。我注意到您在step_addendum中进行了版本控制。您是否预计在requirement中需要版本?您是否预计您的某些要求会在不同时间进行版本控制?是否可以创建step表以涵盖三个version列,以便在version表和相关表格中只有一个version_id

在任何情况下,假设您无法对此表执行任何重大操作,而您只需要提取查询...

我的建议

你实际上并没有根据需要定义什么""是的,所以你需要确定什么"尽可能深的"手段。如果你有一种公式化的方法来发现这个,你可以通过创建像@RandomSeed建议的递归过程来相对容易地完成这个。如果你没有特别公式化的方法来确定什么"尽可能深的"是,您要根据客户定义的级别来确定吗?如果是这样,您仍然可以依赖存储过程。

如果您希望根据具体情况做出这些决定,或者您怀疑这些要求可能会发生变化,您还可以考虑将信息硬编码到存储元数据的MySQL表中用于您的架构。这可以允许您通过在此元数据表上运行SQL requirement查询来确定接收回答的步数。您可以轻松地分配每个步骤或每个方案,以便进行一些您想要完成的递归。您可以在存储过程中使用此信息。

如果你可以改变你的桌子......

更改表格的好处是您可以创建SELECTstep_has_requirement的终端。然后每条记录都有一个"是" (1)或者" no" (0)确定是否涉及要求。然后,您可以在闲暇时对任何1或0的记录运行查询。

requirement_has_requirement

查询现在变得像在状态0和状态1之间切换一样简单,以查找记录。您可以同时查询层次结构中的所有元素。

请注意,如果requirement_has_requirement id | status | major_version | mid_version | minor_version .. | 0 | NULL | NULL | NULL .. | 1 | .. | .. | .. step_has_requirement id | status | ... step位于一个表中,并且根据它是requirement还是{requirement而在单独的表中包含其他列,则此操作会变得更容易并且重复次数更少{1}}。毫无疑问,这有一些缺点,但也有好处。如果您能够在第1卷之前进行这些更改,那么这可能是有益的。

结论

你在开发中显然有一个非常复杂的应用程序。不幸的是,复杂程度超出了你的架构。没有最好的"在不对架构进行一些细微更改的情况下完成所需内容的方法。如果当前不能更改您的架构,那么我强烈建议您快速入侵,以免浪费更多时间并尽快提出调整架构的请求,然后再为您带来更大的麻烦。

答案 1 :(得分:0)

这是一团糟,但我对它采取了一个裂缝。通过使用CTE将其分解为块,您可以对较小的块进行故障排除。

;WITH 

cteRequirement (id) AS
(   SELECT      distinct requirement.id
    FROM        requirement
    INNER JOIN  step_has_requirement 
        ON      requirement.id                  = step_has_requirement.requirement_id
    INNER JOIN  step 
        ON      step.id                         = step_has_requirement.step_id
        AND     step.test_case_id               = step_has_requirement.step_test_case_id
        AND     step.test_case_test_id          = step_has_requirement.step_test_case_test_id
    INNER JOIN  test_case 
        ON      test_case.id                    = step.test_case_id
        AND     test_case.test_id               = step.test_case_test_id
    INNER JOIN  test 
        ON      test.id = test_case.test_id
    INNER JOIN  test_plan_has_test 
        ON      test_plan_has_test.test_id      = test.id
    INNER JOIN  test_plan 
        ON      test_plan.id                    = test_plan_has_test.test_plan_id
        AND     test_plan.test_project_id       = test_plan_has_test.test_plan_test_project_id
    INNER JOIN  test_project 
        ON      test_project.id                 = test_plan.test_project_id
    INNER JOIN  requirement_status 
        ON      requirement_status.id           = requirement.requirement_status_id
    INNER JOIN  requirement_spec_node 
        ON      requirement_spec_node.id                                = requirement.requirement_spec_node_id
        AND     requirement_spec_node.requirement_spec_project_id       = requirement.requirement_spec_node_requirement_spec_project_id
        AND     requirement_spec_node.requirement_spec_spec_level_id    = requirement.requirement_spec_node_requirement_spec_spec_level_id
        AND     requirement_spec_node.requirement_spec_id               = requirement.requirement_spec_node_requirement_spec_id
    INNER JOIN  requirement_spec 
        ON      requirement_spec.id             = requirement_spec_node.requirement_spec_id
        AND     requirement_spec.project_id     = requirement_spec_node.requirement_spec_project_id
        AND     requirement_spec.spec_level_id  = requirement_spec_node.requirement_spec_spec_level_id
    INNER JOIN  project 
        ON      project.id                  = requirement_spec.project_id
    WHERE       requirement_status.[status] = 'general.approved'
        and     (project.id = $P{target_project_id} or project.parent_project_id = $P{target_project_id})
),

cteParent (id) AS
(   SELECT      DISTINCT parent_requirement_id
    FROM        requirement_has_requirement
    INNER JOIN  cteRequirement
        ON      cteRequirement.id = requirement_has_requirement.requirement_id
    WHERE       parent_requirement_id = requirement.id
),

ListOfRequirementIDs (requirement_id) AS
(   SELECT id FROM cteRequirement UNION
    SELECT id FROM cteParents
),

ListOfUniqueIDs (unique_id) AS
(   SELECT      DISTINCT unique_id
    FROM        requirement_status
    INNER JOIN  requirement 
        ON      requirement.requirement_status_id           = requirement_status.id
    INNER JOIN  requirement_spec_node 
        ON      requirement_spec_node.id                    = requirement.requirement_spec_node_id
        AND     requirement_spec_node.requirement_spec_id   = requirement.requirement_spec_node_requirement_spec_id
    INNER JOIN  requirement_spec 
        ON      requirement_spec_node.requirement_spec_id   = requirement_spec.id
        AND     requirement_spec.spec_level_id              = requirement_spec_node.requirement_spec_spec_level_id
        AND     requirement_spec.project_id                 = requirement_spec_node.requirement_spec_project_id
    INNER JOIN  project 
        ON      project.id                                  = requirement_spec.project_id
    INNER JOIN  spec_level 
        ON      spec_level.id                               = requirement_spec.spec_level_id
    WHERE       requirement_status.status = 'general.approved'
        and     (project.id = $P{target_project_id} or project.parent_project_id = $P{target_project_id})
)

SELECT      'Covered' AS [Type], COUNT(ListOfRequirementIDs.requirement_id) AS Cnt
FROM        ListOfRequirementIDs
UNION ALL
SELECT      'Total' AS [Type],   COUNT(ListOfUniqueIDs.unique_id) AS Cnt
FROM        ListOfUniqueIDs