衡量SQL语句的复杂性

时间:2010-07-28 14:02:52

标签: sql static-analysis cyclomatic-complexity

大多数编程语言中方法的复杂性可以使用静态源代码分析器以圈复杂度来衡量。是否有类似的指标来衡量SQL查询的复杂性?

测量返回查询所需的时间很简单,但是如果我只想量化查询的复杂程度呢?

[编辑/注] 虽然获得执行计划很有用,但在这种情况下,这并不一定是我想要识别的。我不是在寻找服务器执行查询的难度,我正在寻找一个指标来确定开发人员编写查询的难度,以及包含缺陷的可能性。

[编辑/注释2] 无可否认,有时候测量复杂性没有用,但有时也是如此。有关该主题的进一步讨论,请参阅this question

12 个答案:

答案 0 :(得分:10)

我不确定查询计划的检索是否会回答这个问题:查询计划隐藏了在返回(或在过滤器中)之前对数据执行的计算的复杂性的一部分;查询计划要求有意义的数据库是相关的。事实上,复杂性和执行时间有些相反;类似“好,快,便宜 - 挑选任何两个”。

最终是关于犯错的可能性,还是不理解我写的代码?

类似的东西:

  • 表次数(1
  • 每个连接表达式+1(每个外部连接+1)?
  • WHEREHAVING
  • 之后的每个谓词+1
  • 每个GROUP BY表达式+1
  • +1 UNIONINTERSECT
  • 每个函数调用+1
  • 每个CASE表达式+1

答案 1 :(得分:10)

软件复杂性的常用度量包括Cyclomatic Complexity(控制流程复杂程度的度量)和Halstead complexity(算术复杂程度)。

SQL查询中的“控制流”最好与查询中的“和”和“或”运算符相关。

“计算复杂度”最好与SUM或隐式JOINS等运算符相关。

一旦您决定如何对SQL查询的每个语法单元进行分类,无论它是“控制流”还是“计算”,您都可以直接计算Cyclomatic或Halstead度量。

SQL优化器对查询认为所做的事情绝对无关紧要。复杂性度量的目的是表征一个人理解查询的难度,而不是如何有效地评估它。

同样,DDL所说的内容或是否涉及观点不应包含在此类复杂性措施中。这些指标背后的假设是,当你简单地调用它时,使用抽象中的机器的复杂性并不重要,因为可能是抽象做了编码器很好理解的事情。这就是为什么Halstead和Cyclomatic测量在计数中不包括被调用的子程序的原因,我认为你可以提出一个很好的例子,即视图和DDL信息是那些“被调用的”抽象。

最后,这些复杂性数字的完美正确性或完全错误并不重要,只要它们反映了复杂性的一些真实性,并且可以将它们相对于彼此进行比较。通过这种方式,您可以选择最复杂的SQL片段,从而对它们进行排序,并将测试注意力集中在最复杂的片段上。

答案 2 :(得分:4)

请随意尝试我的脚本,该脚本概述了存储过程的大小,对象依赖项的数量和参数的数量 -

Calculate TSQL Stored Procedure Complexity

答案 3 :(得分:2)

SQL查询是声明性的而不是程序性的:它们没有指定如何实现其目标。 SQL引擎将创建一个程序攻击计划,这可能是寻找复杂性的好地方。尝试检查EXPLAIN(或EXPLAIN PLAN)语句的输出,它将粗略地描述引擎用于执行查询的步骤。

答案 4 :(得分:1)

嗯,我不知道有任何工具做过这样的事情,但在我看来,使查询更复杂的原因是: 连接数 条件的数量 功能的数量 子查询的数量 转换为不同数据类型的数量 案例陈述的数量 循环或游标的数量 交易中的步骤数

然而,虽然更复杂的查询可能看起来是那些具有最多可能缺陷的查询,但我发现简单查询很可能包含缺陷,因为它们更可能是由某人编写的。不了解数据模型,因此它们似乎可以正常工作,但实际上返回错误的数据。所以我不确定这样的指标会告诉你多少。

答案 5 :(得分:0)

如果您正在使用SQL Server,我会说您应该查看执行计划中的查询成本(特别是子树成本)。

Here是一个链接,它涵盖了您应该在执行计划中查看的一些内容。

答案 6 :(得分:0)

根据您的RDBMS,可能有一些查询计划工具可以帮助您分析RDBMS在获取查询时将采取的步骤。

SQL Server Management Studio Express具有内置查询执行计划。 Pervasive PSQL有其查询计划查找器。 DB2有类似的工具(忘了它们被称为)。

答案 7 :(得分:0)

一个好问题。问题是对于SQL查询,如:

SELECT * FROM foo;

复杂性可能取决于“foo”是什么以及数据库实现。对于像这样的函数:

int f( int n ) {
   if ( n == 42 ) {
      return 0;
   }
   else {
      return n;
   }
}

没有这种依赖。

但是,我认为应该可以为SELECT提供一些有用的指标,即使它们不是很精确,我也会有兴趣看看它得到了什么答案。

答案 8 :(得分:0)

在没有任何工具可以做到这一点的情况下,务实的方法是确保被分析的查询一致地格式化,然后计算代码行。

或者在保存到文件时使用以字节为单位的查询大小(注意所有查询都使用相同的字符编码保存)。

在我没有任何其他想法的情况下,并不是很聪明,但却是复杂的合理代理。

答案 9 :(得分:0)

这是一个简单算法的想法,用于计算与查询的可读性相关的复杂性得分:

  1. 在查询上应用一个简单的词法分析器(例如在文本编辑器中或在SO上用于语法着色的词法分析器),以将查询分为令牌并为每个令牌提供一个类:
    • SQL关键字
    • SQL函数名称
    • 带有字符转义符的字符串文字
    • 不带字符转义的字符串文字
    • 日期或日期+时间的字符串文字
    • 数字文字
    • 逗号
    • 括号
    • SQL注释(-,/ * ... * /)
    • 引用用户单词
    • 未引用用户文字:其他所有内容
  2. 为每个令牌赋予分数,并为每个类使用不同的权重(对于SQL关键字使用不同的权重)。
  3. 添加每个令牌的分数。
  4. 完成。

这应该很好地工作,例如计算子查询就像计算SELECTFROM关键字的数量。

通过将此算法与不同的权重表一起使用,您甚至可以测量不同维度的复杂性。例如,在查询之间进行细微的比较。或者,为使用特定于SQL引擎的关键字或函数(例如MySQL上的GROUP_CONCAT)的查询评分更高。

还可以对算法进行调整,以考虑到SQL关键字的情况:如果它们的大小写不一致,则会增加复杂性。或考虑缩进(回车,关键字在一行中的位置)

注意:我受到@redcalx答案的启发,该答案建议应用标准格式化程序并计算代码行数。但是,我的解决方案比较简单,因为它没有构建完整的AST(抽象语法树)。

答案 10 :(得分:0)

在编程语言中,我们有几种方法来计算时间复杂度或空间复杂度。

类似地,我们可以与sql进行比较,就像在过程中那样,您所拥有的循环行数与编程语言类似,但是与通常在sql编程语言中仅输入不同的是,它将与输入一起完全取决于其中的数据表/视图等的操作以及查询本身的开销复杂度。

像一个简单的逐行查询

   Select * from table ; 
  // This will totally depend on no of 
       records say n hence O(n)

   Select max(input) from table;
   // here max would be an extra 
   overhead added to each 
   Therefore t*O(n) where t is max 
   Evaluation time

答案 11 :(得分:-1)

考虑复杂性就足够了,就像您自己编写查询代码一样。 如果表有N行,那么

  1. 一个简单的SELECT将是O(N)
  2. ORDER BY是O(NlogN)
  3. JOIN为O(N * M)
  4. DROP TABLE为O(1)
  5. 一个SELECT DISTINCT是O(N ^ 2)
  6. 查询 1 NOT IN / IN查询 2 将为O(O 1 (N)* O 2 (N))