查找 SQL 查询中未使用的联接

发布于 2024-10-30 13:28:01 字数 186 浏览 8 评论 0原文

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

我正在寻找一种工具(或除了 eyes+brain 之外的任何工具),在给定 SQL 查询的情况下,它可以分析哪些连接表在 SELECT 部分中没有选择字段。

你知道这样的工具吗?

谢谢

I am currently maintaining a significant number of SQL queries. Some of them are created by copy/paste operations, then removing unnecessary fields and sometimes forgetting to remove the tables where these fields come from.

I am looking for a tool (or anything apart from eyes+brain) that, given a SQL query, would analyze which of the joined tables have no field selected in the SELECT part.

Do you know of such a tool?

Thank you

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

无声无音无过去 2024-11-06 13:28:01

假设一个工具可能存在,但只有在满足所述连接的所有以下条件时才能保证它是正确的。

  • 它是左连接或外连接或内连接,其中已知基数为 1-1 并且...
  • 它不是在 SELECT、HAVING、GROUP BY 或 WHERE 中引用并且...
  • 它不是具有副作用的 JOIN 函数...

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

Hypothetically a tool could exist but it would only be guaranteed to be correct if all the following criteria where met for said join

  • Its A LEFT or OUTER JOIN or an INNER JOIN where the cardinality where known to be 1-1 And...
  • Its Not Referenced in a SELECT, HAVING, GROUP BY or WHERE and...
  • It is not a JOIN to function that has a side effect...

Probably why there's no deterministic warnings in SQL parsers the way there is for let's say an unused variable in C#. But it might be worth while to create a SQL checker that looks for some of these conditions and lets the user know that there's a possibility for optimization here.

悸初 2024-11-06 13:28:01

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

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

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

中不会返回 Sales 表中的任何列,但联接对于返回正确的结果集至关重要。

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

Just because no fields are referenced in the SELECT, that does not mean that the join is not important to the logic of the query and results could change if the join is removed.

Consider this simple example: Return the name of all customers who purchased an item in 2011.

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

No columns from the Sales table are returned in the SELECT, yet the join is critical to returning the correct result set.

Bottom line: I think you'll need a human eye/brain code review to clean things up properly.

苯莒 2024-11-06 13:28:01

下面的函数用 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;
    }

below function replaces all select fields with count(*) and 2nd part removes unnecessary joins. This function works only with tables that has aliases and should be tested for very complex queries and wont work if there is inner queries in join condition.

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;
    }
隔纱相望 2024-11-06 13:28:01

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

也就是说,识别冗余的 LEFT JOIN 是可能的。我正在使用这个自动查询优化器来自动优化 SQL 查询。除此之外,它还可以识别冗余的左连接。

As mentioned above, identifying redundant INNER JOINs will be a problem, as sometimes they have an impact on the returned data, even if no data is actually selected from those tables.

Said that, identifying redundant LEFT JOINs is possible. I'm using this automatic query optimizer to optimize SQL queries automatically. Among other things, it can identify redundant left joins.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文