【问题标题】:Finding unused join in a SQL query在 SQL 查询中查找未使用的联接
【发布时间】:2011-07-30 21:30:48
【问题描述】:

我目前正在维护大量的 SQL 查询。其中一些是通过复制/粘贴操作创建的,然后删除不必要的字段,有时忘记删除这些字段来自的表。

我正在寻找一种工具(或除了眼睛+大脑之外的任何东西),给定一个 SQL 查询,它可以分析哪些连接的表在 SELECT 部分没有选择字段。

你知道这样的工具吗?

谢谢

【问题讨论】:

    标签: sql query-optimization analysis


    【解决方案1】:

    假设一个工具可能存在,但只有满足以下所有条件才能保证该工具的正确性

    • 它是 LEFT 或 OUTER JOIN 或 INNER JOIN,其中基数已知为 1-1 并且...
    • 它未在 SELECT、HAVING、GROUP BY 或 WHERE 中引用...
    • 这不是 JOIN 到有副作用的函数...

    可能为什么 SQL 解析器中没有确定性警告,就像 C# 中未使用的变量一样。但可能值得创建一个 SQL 检查器来查找其中一些条件并让用户知道这里有优化的可能性。

    【讨论】:

    • 您还需要确保它不是在 SUB-SELECT 或相关子查询中引用...
    • 谢谢,我接受您的回答,因为您为构建此类工具提供了更多标准。
    【解决方案2】:

    仅仅因为 SELECT 中没有引用任何字段,这并不意味着连接对查询逻辑不重要,并且如果删除连接,结果可能会发生变化。

    考虑这个简单的示例:返回 2011 年购买商品的所有客户的姓名。

    SELECT DISTINCT c.CustomerName
        FROM Customer c
            INNER JOIN Sales s
                ON c.CustomerID = s.CustomerID
                    AND s.SalesDate >= '2011-01-01'
    

    在 SELECT 中没有返回 Sales 表中的列,但连接对于返回正确的结果集至关重要。

    底线:我认为您需要人眼/大脑代码审查才能正确清理。

    【讨论】:

    • 我很清楚这一点,我问这个问题是因为我知道在我的情况下,连接只是为了检索额外的数据。不管怎么说,还是要谢谢你。顺便说一句,如果您能提供原始问题的答案,我将不胜感激:)
    • @Johann Blais:我相信我用答案的最后一句话回答了你原来的问题,但我在这里再次改写:没有工具,你需要手动编码审查。
    • 谢谢,我想我会自己制作工具的。
    【解决方案3】:

    下面的函数用 count(*) 替换所有选择字段,第二部分删除不必要的连接。此函数仅适用于具有别名的表,并且应该针对非常复杂的查询进行测试,如果在连接条件下存在内部查询,则该函数将不起作用。

    function sql_query_count($sql) {
            //replace select fields with count(*)
            $a = true;
            $b = 0;
            $first_select = stripos($sql, 'select ');
            $last_from = 0;
            $i = 0;
            while($a){
                $i++;
                $b = stripos($sql, ' from ',$last_from);
                $c = strripos(substr($sql, $last_from, $b), 'select ');
                if ($c == $first_select || $c === false || $i>100) $a = false;
                $last_from = $b+6;
            }        
            if (stripos($sql, 'order by') !== false)
                $sql = substr($sql, 0, stripos($sql, 'order by'));
            $sql1 = 'select count(*) as c ' . substr($sql, $b);
    
            //remove unnecessary joins
            $joins = preg_split("/ join /i", $sql1);
            $join_count = count($joins);
            $join_type = '';
            if (count($joins)>1){
                for ($index = 0; $index < $join_count+2; $index++) {
                    $sql_new = '';
                    $where = '';
                    $i = 0;
                    foreach ($joins as $key => $value) { $i++;
                        $parts = preg_split("/ where /i", trim($value));
                        $value = $parts[0];
                        unset($parts[0]);
                        $where = implode(' where ', $parts);
                        $occurence_count = 0;
                        if ($i > 1) {
                            $a = explode(' on ', $value);
                            $c = preg_replace('!\s+!', ' ', trim($a[0]));
                            $c = explode(' ', $c);
                            $occurence_count = substr_count($sql1, ' '.$c[1].'.')+substr_count($sql1, '='.$c[1].'.');
                        }
                        $t = explode(' ', $value);
                        $j = '';
                        if (trim(strtolower($t[count($t) - 1])) == 'inner'){
                            $j = 'inner';
                            unset($t[count($t) - 1]);
                        } else if (trim(strtolower($t[count($t) - 2])).' '.trim(strtolower($t[count($t) - 1])) == 'left outer'){
                            $j = 'left outer';
                            unset($t[count($t) - 1]);
                            unset($t[count($t) - 1]);
                        }
                        if ($occurence_count == 0 || $occurence_count > 1) $sql_new.= ' '.$join_type.(($join_type!='')?' join ':'').implode(' ', $t);                    
                        $join_type = $j;
                    }
                    $sql_new .= ' where '.$where;
                    $sql1 = $sql_new;
                    $joins = preg_split("/ join /i", $sql1);
                }
            }
            return $sql1;
        }
    

    【讨论】:

      【解决方案4】:

      如上所述,识别冗余 INNER JOIN 将是一个问题,因为有时它们会影响返回的数据,即使实际上没有从这些表中选择数据。

      也就是说,识别冗余的 LEFT JOIN 是可能的。我正在使用automatic query optimizer 自动优化 SQL 查询。除其他外,它还可以识别冗余左连接。

      【讨论】:

        猜你喜欢
        • 2021-10-06
        • 1970-01-01
        • 2021-07-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多