Регулярное выражение для поиска всех имен таблиц в запросе - PullRequest
7 голосов
/ 11 ноября 2008

Я не такой горячий в регулярных выражениях, и это заставило мой маленький ум немного растаять.

Я пытаюсь найти все имена таблиц в запросе. Скажем, у меня есть запрос:

SELECT one, two, three FROM table1, table2 WHERE X=Y

Я хотел бы вытащить "table1, table2" или "table1" и "table2"

Но что, если нет утверждения "где"? Это может быть конец файла, или может быть группа или порядок и т. Д. Я знаю, что «большую часть времени» это не будет проблемой, но мне не нравится идея кодирования для «большинства» ситуации и зная, что я оставил дыру, которая может привести к ошибкам позже.

Это выполнимое выражение Regex? Я являюсь игроком Регекса?

(П.С. это будет сделано в C #, но предполагается, что это не имеет большого значения).

Ответы [ 12 ]

13 голосов
/ 11 ноября 2008

RegEx не очень хорош в этом, так как он намного сложнее, чем кажется:

  • Что если они используют соединения LEFT / RIGHT INNER / OUTER / CROSS / MERGE / NATURAL вместо синтаксиса a, b? В любом случае следует избегать синтаксиса a, b.
  • А как насчет вложенных запросов?
  • Что делать, если нет таблицы (выбор константы)
  • А как насчет разрывов строк и другого форматирования пробелов?
  • Псевдоним?

Я мог бы продолжить.

Что вы можете сделать, так это найти анализатор SQL и выполнить свой запрос через него.

5 голосов
/ 11 ноября 2008

Все говорит о полезности такого регулярного выражения в контексте SQL. Если вы настаиваете на регулярном выражении, и ваши операторы SQL всегда выглядят так, как вы показали (это означает, что нет подзапросов, объединений и т. Д.), Вы можете использовать

FROM\s+([^ ,]+)(?:\s*,\s*([^ ,]+))*\s+ 
3 голосов
/ 12 февраля 2010

Я довольно поздно опаздываю на вечеринку, однако я подумал, что хочу поделиться регулярным выражением, которое я сейчас использую для анализа всех объектов нашей базы данных, и я не согласен с мнением, что это невозможно сделать с помощью одного.

регулярное выражение имеет несколько предположений

1) Вы не используете стиль синтаксиса соединения A, B

2) Какой бы анализатор регулярных выражений вы не использовали, он игнорирует регистр.

3) Вы анализируете, выбираете, объединяете, обновляете, удаляете и усекаете. Он не поддерживает вышеупомянутый MERGE / NATURAL, потому что мы их не используем, однако я уверен, что дальнейшую поддержку добавить будет не сложно.

Мне хотелось бы знать, к какому типу транзакций относится таблица, поэтому я включил группы Named Capture, чтобы сообщить мне.

Теперь я давно не использую регулярные выражения, поэтому, возможно, есть улучшения, которые можно сделать, однако до сих пор во всех моих тестах это точно.

\bjoin\s+(?<Retrieve>[a-zA-Z\._\d]+)\b|\bfrom\s+(?<Retrieve>[a-zA-Z\._\d]+)\b|\bupdate\s+(?<Update>[a-zA-Z\._\d]+)\b|\binsert\s+(?:\binto\b)?\s+(?<Insert>[a-zA-Z\._\d]+)\b|\btruncate\s+table\s+(?<Delete>[a-zA-Z\._\d]+)\b|\bdelete\s+(?:\bfrom\b)?\s+(?<Delete>[a-zA-Z\._\d]+)\b
2 голосов
/ 04 июня 2012

Одним из обходных путей является внедрение соглашения об именах для таблиц и представлений. Затем оператор SQL может быть проанализирован по префиксу именования.

Например:

SELECT tbltable1.one, tbltable1.two, tbltable2.three
FROM tbltable1
    INNER JOIN  tbltable2
        ON tbltable1.one = tbltable2.three

Разделить пробел на массив:

("SELECT","tbltable1.one,","tbltable1.two,","tbltable2.three","FROM","tbltable1","INNER","JOIN","tbltable2","ON","tbltable1.one","=","tbltable2.three")

Получить слева от элементов к периоду:

("SELECT","tbltable1","tbltable1","tbltable2","FROM","tbltable1","INNER","JOIN","tbltable2","ON","tbltable1","=","tbltable2")

Удалить элементы с символами:

("SELECT","tbltable1","tbltable1","tbltable2","FROM","tbltable1","INNER","JOIN","tbltable2","ON","tbltable1","tbltable2")

Уменьшить до уникальных значений:

("SELECT","tbltable1","tbltable2","FROM","INNER","JOIN","ON")

Фильтр слева 3 символа = "tbl"

("tbltable1","tbltable2")

2 голосов
/ 11 ноября 2008

Я нашел этот сайт с отличным парсером!

http://www.sqlparser.com/

хорошо стоит. Работает угощение.

1 голос
/ 11 ноября 2008

Я думаю, что было бы проще разбить строку на строку и найти ключевые слова SQL, которые могли бы связать имена таблиц. Вы знаете, что имена будут следовать за FROM, но за ними могут следовать WHERE, GROUP BY, HAVING или вообще без ключевого слова, если они находятся в конце запроса.

1 голос
/ 11 ноября 2008

Это определенно не легко.

Рассмотрим подзапросы.

select
  *
from
  A
  join (
    select
       top 5 *
    from
      B)
    on B.ID = A.ID
where
  A.ID in (
    select
      ID
    from
      C
    where C.DOB = A.DOB)

В этом запросе используются три таблицы.

0 голосов
/ 20 января 2016

Я использовал этот код в качестве макроса Excel для синтаксического анализа выбора и извлечения имен таблиц.

Мой анализ предполагает, что синтаксис select from a , b , c не используется.

Просто запустите его по вашему запросу SQL, и если вы не удовлетворены результатом, вы должны быть всего в нескольких строках кода от ожидаемого результата. Просто отладьте и измените код соответствующим образом.

Sub get_tables()
    sql_query = Cells(5, 1).Value
    tables = ""

    'get all tables after from
    sql_from = sql_query

    While InStr(1, UCase(sql_from), UCase("from")) > 0

        i = InStr(1, UCase(sql_from), UCase("from"))
        sql_from = Mid(sql_from, i + 5, Len(sql_from) - i - 5)
        i = InStr(1, UCase(sql_from), UCase(" "))

        While i = 1

            sql_from = Mid(sql_from, 2, Len(sql_from) - 1)
            i = InStr(1, UCase(sql_from), UCase(" "))

        end

        i = InStr(1, sql_join, Chr(9))

        While i = 1

            sql_join = Mid(sql_join, 2, Len(sql_join) - 1)
            i = InStr(1, sql_join, Chr(9))

        end

        a = InStr(1, UCase(sql_from), UCase(" "))
        b = InStr(1, sql_from, Chr(10))
        c = InStr(1, sql_from, Chr(13))
        d = InStr(1, sql_from, Chr(9))

        MinC = a

        If MinC > b And b > 0 Then MinC = b
        If MinC > c And c > 0 Then MinC = c
        If MinC > d And d > 0 Then MinC = d

        tables = tables + "[" + Mid(sql_from, 1, MinC - 1) + "]"

    end

    'get all tables after join
    sql_join = sql_query

    While InStr(1, UCase(sql_join), UCase("join")) > 0

        i = InStr(1, UCase(sql_join), UCase("join"))
        sql_join = Mid(sql_join, i + 5, Len(sql_join) - i - 5)
        i = InStr(1, UCase(sql_join), UCase(" "))

        While i = 1

            sql_join = Mid(sql_join, 2, Len(sql_join) - 1)
            i = InStr(1, UCase(sql_join), UCase(" "))

        end

        i = InStr(1, sql_join, Chr(9))

        While i = 1

            sql_join = Mid(sql_join, 2, Len(sql_join) - 1)
            i = InStr(1, sql_join, Chr(9))

        end

        a = InStr(1, UCase(sql_join), UCase(" "))
        b = InStr(1, sql_join, Chr(10))
        c = InStr(1, sql_join, Chr(13))
        d = InStr(1, sql_join, Chr(9))

        MinC = a

        If MinC > b And b > 0 Then MinC = b
        If MinC > c And c > 0 Then MinC = c
        If MinC > d And d > 0 Then MinC = d

        tables = tables + "[" + Mid(sql_join, 1, MinC - 1) + "]"

    end

    tables = Replace(tables, ")", "")
    tables = Replace(tables, "(", "")
    tables = Replace(tables, " ", "")
    tables = Replace(tables, Chr(10), "")
    tables = Replace(tables, Chr(13), "")
    tables = Replace(tables, Chr(9), "")
    tables = Replace(tables, "[]", "")

End Sub
0 голосов
/ 27 октября 2011

В PHP я использую эту функцию, она возвращает массив с именами таблиц, использованными в выражении sql:

function sql_query_get_tables($statement){
    preg_match_all("/(from|into|update|join) [\\'\\´]?([a-zA-Z0-9_-]+)[\\'\\´]?/i",
            $statement, $matches);
    if(!empty($matches)){
        return array_unique($matches[2]);
    }else return array();
}

Обратите внимание, что он не работает с соединениями a, b или именами schema.tablename

Я надеюсь, что это работает для вас

0 голосов
/ 03 июня 2011

Я пробовал все вышеперечисленное, но ни один из них не работал, так как я использую широкий спектр запросов. Я работаю с PHP, хотя и использовал библиотеку PEAR под названием SQL_Parser, но надеюсь, что мое решение поможет. Кроме того, у меня были проблемы с апострофами, и MySQL зарезервировал sencences, поэтому я решил убрать все разделы полей из запроса перед его анализом.

function getQueryTable ($query) {
    require_once "SQL/Parser.php";
    $parser = new SQL_Parser();
    $parser->setDialect('MySQL');

    // Stripping fields section
    $queryType = substr(strtoupper($query),0,6);            
    if($queryType == 'SELECT') { $query  = "SELECT * ".stristr($query, "FROM"); }
    if ($havingPos = stripos($query, 'HAVING')) { $query = substr($query, 0, $havingPos); }


    $struct = $parser->parse($query);

    $tableReferences = $struct[0]['from']['table_references']['table_factors'];

    foreach ((Array) $tableReferences as $ref) {
        $tables[] = ($ref['database'] ? $ref['database'].'.' : $ref['database']).$ref['table'];
    }

    return $tables;

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...