Поиск неиспользуемого соединения в запросе SQL - PullRequest
5 голосов
/ 06 апреля 2011

В настоящее время я поддерживаю значительное количество запросов SQL. Некоторые из них создаются операциями копирования / вставки, после чего удаляются ненужные поля, а иногда и забывается удалить таблицы, откуда поступают эти поля.

Я ищу инструмент (или что-то, кроме глаз + мозга), который, учитывая SQL-запрос, проанализировал бы, в какой из соединенных таблиц не выбрано поле в части SELECT.

Знаете ли вы о таком инструменте?

Спасибо

Ответы [ 4 ]

6 голосов
/ 06 апреля 2011

То, что в SELECT нет ссылок на поля, не означает, что объединение не имеет значения для логики запроса, и результаты могут измениться, если объединение будет удалено.

Рассмотрим простой пример: верните имя всех клиентов, которые приобрели товар в 2011 году.

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

Никакие столбцы из таблицы Sales не возвращаются в SELECT, однако объединение имеет решающее значение для возврата правильного набора результатов.

Итог: я думаю, что вам понадобится проверка кода человеческого глаза / мозга, чтобы очистить вещи должным образом.

5 голосов
/ 06 апреля 2011

Гипотетически инструмент мог бы существовать, но он гарантированно был бы верным, если бы все следующие критерии были выполнены для указанного соединения

  • Это ЛЕВОЕ или НАРУЖНОЕ СОЕДИНЕНИЕ или ВНУТРЕННЕЕ СОЕДИНЕНИЕ, гдеизвестен как 1-1 и ...
  • Он не указан в SELECT, HAVING, GROUP BY или WHERE и ...
  • Это не соединение с функцией, имеющей сторонуэффект ...

Возможно, почему в синтаксических анализаторах SQL нет детерминированных предупреждений, как, например, неиспользуемая переменная в C #.Но, возможно, стоит создать средство проверки SQL, которое ищет некоторые из этих условий и позволяет пользователю знать, что здесь есть возможность для оптимизации.

0 голосов
/ 29 ноября 2017

Как упомянуто выше, выявление избыточных ВНУТРЕННИХ СОЕДИНЕНИЙ будет проблемой, поскольку иногда они влияют на возвращаемые данные, даже если в этих таблицах данные фактически не выбираются.СОЕДИНЕНИЯ возможны.Я использую автоматический оптимизатор запросов для автоматической оптимизации запросов SQL.Среди прочего, он может идентифицировать избыточные левые соединения.

0 голосов
/ 10 октября 2017
Функция

ниже заменяет все выбранные поля на количество (*), а вторая часть удаляет ненужные объединения.Эта функция работает только с таблицами, которые имеют псевдонимы и должны быть проверены на очень сложные запросы и не будут работать, если в условии соединения есть внутренние запросы.

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;
    }
...