Разделить несколько операторов SQL на отдельные операторы SQL - PullRequest
1 голос
/ 11 марта 2009

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

Вот ситуация: У меня есть блок команд SQL в виде простого текста. Это может быть одна команда SQL или несколько. Мне нужен способ разделить несколько команд SQL, чтобы я мог запускать их по одной за раз. Microsoft SQL Management Studio имеет такое поведение из коробки.

Я пытаюсь добавить эту функцию в приложение PHP5 / MySQL5, работающее на Apache (Debian).

Некоторые важные моменты:

  1. Мне действительно нужно запускать их по одному. Серьезно.
  2. Я не хочу требовать, чтобы пользователь вводил точку с запятой после каждого оператора SQL.
  3. Операторы SQL могут состоять из одной или нескольких строк, поэтому я не могу переносить их на LB / CR
  4. Требуется как минимум поддержка SELECT, UPDATE, INSERT, DELETE.
  5. Он должен поддерживать запросы, которые выбираются суб-выбором
  6. Нужно работать аккуратно с вкладками SQL
  7. (В интересах используемого программного обеспечения) Я не хочу заставлять пользователя вводить какой-либо разделитель.

Вот пример блока SQL, который мне нужно разделить на два оператора:

select sMessage, 
(
    SELECT COUNT(sTag) FROM Tags WHERE ixTicket = note.ixTicket
) FROM note
select * from ticket
    WHERE (SELECT MAX(nCount) FROM Counter WHERE ixTicket = ticket.ixTicket) > 5

Я попробовал несколько попыток RegEx, но это не кажется достаточно мощным.

Любая рекомендация о подходе к решению этой проблемы?

Ответы [ 10 ]

3 голосов
/ 11 марта 2009

Я не уверен, что это вообще возможно. Вам, безусловно, потребуются глубокие знания синтаксиса SQL вашей целевой СУБД. Например, прямо у меня в голове это единственное утверждение MySQL:

INSERT INTO things
SELECT * FROM otherthings ON DUPLICATE KEY
UPDATE thingness=thingness+1

Вероятно, в некоторых СУБД есть конструкции, которые без разделителя могут быть неоднозначными.

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

Я думаю, тебя могут заставить. Это совершенно стандартный способ разграничения операторов SQL. Даже если вы можете найти эвристику для определения точек, которые, вероятно, в начале SQL-запроса, вы рискуете привести к катастрофическим последствиям, таким как случайное «УДАЛЕНИЕ ИЗ вещей» - без предложения WHERE.

Операторы SQL могут состоять из одной или нескольких строк, поэтому я не могу переносить их в LB / CR

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

Я попробовал несколько попыток RegEx, но это не кажется достаточно мощным.

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

';'
";"
`;`
'\';'
''';'
-- ;
#;
/*;*/

и любое взаимное расположение этих структур. Ик!

1 голос
/ 10 декабря 2011
    $sMultiQuery = 'SHOW TABLES; SELECT * FROM `test`';
    $aQueries    = array();

    if ( preg_match_all('/([^;]*?((\'.*?\')|(".*?"))?)*?(;\s*|\s*$)/', $sMultiQuery, $aMatches) )
    {
        $aQueries = $aMatches[0];
    }
    else
    {
        $aQueries = array($sMultiQuery);
    }

    foreach ( $aQueries as $sQuery )
    {
        # Do your thing
    }
1 голос
/ 12 мая 2010

может быть со следующим Java Regexp? проверить тест ...

@Test
public void testRegexp() {
    String s = //
        "SELECT 'hello;world' \n" + //
        "FROM DUAL; \n" + //
        "\n" + //
        "SELECT 'hello;world' \n" + //
        "FROM DUAL; \n" + //
        "\n";

    String regexp = "([^;]*?('.*?')?)*?;\\s*";

    assertEquals("<statement><statement>", s.replaceAll(regexp, "<statement>"));
}
1 голос
/ 11 марта 2009

Чтобы добавить в обсуждение причуду, которая периодически вызывает проблемы:

DECLARE c CURSOR FOR
    SELECT * FROM SomeWhere ...
        FOR UPDATE

Завершающее ОБНОВЛЕНИЕ имеет тенденцию выбрасывать специальные парсеры с их шага. Вполне может быть, что вам не нужно беспокоиться об этом, потому что нотация DECLARE (которая на самом деле является встроенным SQL, а не простым SQL) не разрешена с самого начала. Но предложение FOR UPDATE может появляться на некоторых диалектах SQL, даже если оно отсутствует в операторе DECLARE, так что будьте осторожны.

1 голос
/ 11 марта 2009

Может быть, попробуйте эту библиотеку. Я успешно использовал его для разбора sql в прошлом. http://www.sqlparser.com/

0 голосов
/ 11 марта 2009

Вы могли бы разобрать это самостоятельно, я полагаю. Найдите ключевые слова SELECT, DELETE, UPDATE, INSERT, EXEC и т. Д.

Когда вы анализируете, если вы встречаете "(", увеличивайте счетчик: nest_level ++

Если вы встречаете декрет ")" nest_level -

Затем, когда вы встретите ключевое слово и nest_level == 0, вы перейдете к следующему утверждению.

Вам также придется обрабатывать такие случаи, как

 INSERT ...
 SELECT ....

Так что для ВСТАВКИ вы должны искать либо SELECT, либо VALUES ...

И, без сомнения, другие случаи.

Согласитесь с kquinn, вам просто нужна точка с запятой. Я не думаю, что в этом есть что-то "не крутое".

0 голосов
/ 11 марта 2009

Вам необходимо указать точку с запятой. Технически, без него оператор SQL совершенно недопустим; любой, кто его пропускает, пишет некорректный SQL. Требование точки с запятой решает все ваши проблемы стандартным образом и облегчает написание программного обеспечения.

Возможно, сделайте следующее: если пользователь вводит запрос , а не , содержащий одну или несколько точек с запятой (конечно, за пределами кавычек), добавьте точку с запятой в конце и запустите ее как один запрос. В противном случае разделите введенные запросы на точки с запятой и запустите каждый из них по отдельности, возможно, добавив точку с запятой в конце окончательного запроса, если он пропущен.

Это решение простое в написании, соответствует стандарту SQL и просто работает. Отсутствие разделителя - верный путь к безумию.

0 голосов
/ 11 марта 2009

Вы пытались использовать ключевые слова «SELECT», «UPDATE», «INSERT» и «DELETE» в сочетании с подсчетом числа открывающих (и закрывающих скобок)?

Это должно позволить вам определить, избегать вложенных операторов SELECT и найти правильный конец оператора.

0 голосов
/ 11 марта 2009

Если вы не хотите, чтобы ваши пользователи вводили символ-разделитель, например ';' или что-то еще, вам нужно будет самостоятельно проанализировать ввод и иметь логику, чтобы определить, где начинаются операторы.

Ваша логика должна работать с очевидными ключевыми словами, начинающимися с запроса SELECT, UPDATE, INSERT, DELETE, и переходить к следующему ключевому слову (или концу ввода).

0 голосов
/ 11 марта 2009

Лучше всего потребовать от пользователя поставить какой-либо тип разделителя между утверждениями. Например: требуется, чтобы каждый оператор был очерчен строкой, содержащей только слово GO, или "\", или завершить каждый оператор ";".

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

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