【发布时间】:2011-07-30 21:30:48
【问题描述】:
我目前正在维护大量的 SQL 查询。其中一些是通过复制/粘贴操作创建的,然后删除不必要的字段,有时忘记删除这些字段来自的表。
我正在寻找一种工具(或除了眼睛+大脑之外的任何东西),给定一个 SQL 查询,它可以分析哪些连接的表在 SELECT 部分没有选择字段。
你知道这样的工具吗?
谢谢
【问题讨论】:
标签: sql query-optimization analysis
我目前正在维护大量的 SQL 查询。其中一些是通过复制/粘贴操作创建的,然后删除不必要的字段,有时忘记删除这些字段来自的表。
我正在寻找一种工具(或除了眼睛+大脑之外的任何东西),给定一个 SQL 查询,它可以分析哪些连接的表在 SELECT 部分没有选择字段。
你知道这样的工具吗?
谢谢
【问题讨论】:
标签: sql query-optimization analysis
假设一个工具可能存在,但只有满足以下所有条件才能保证该工具的正确性
可能为什么 SQL 解析器中没有确定性警告,就像 C# 中未使用的变量一样。但可能值得创建一个 SQL 检查器来查找其中一些条件并让用户知道这里有优化的可能性。
【讨论】:
仅仅因为 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 表中的列,但连接对于返回正确的结果集至关重要。
底线:我认为您需要人眼/大脑代码审查才能正确清理。
【讨论】:
下面的函数用 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;
}
【讨论】:
如上所述,识别冗余 INNER JOIN 将是一个问题,因为有时它们会影响返回的数据,即使实际上没有从这些表中选择数据。
也就是说,识别冗余的 LEFT JOIN 是可能的。我正在使用automatic query optimizer 自动优化 SQL 查询。除其他外,它还可以识别冗余左连接。
【讨论】: