Intereting Posts

Разделить несколько операторов SQL на отдельные операторы SQL

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

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

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

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

  1. Мне действительно нужно запускать их по одному. Шутки в сторону.
  2. Я не хочу, чтобы пользователь вводил полуточку после каждого оператора SQL.
  3. Операторы SQL могут быть на одной или нескольких строках, поэтому я не могу обернуть LBs / CRs
  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, но это не кажется достаточно мощным.

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

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

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

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

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

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

Операторы SQL могут быть на одной или нескольких строках, поэтому я не могу обернуть LBs / CRs

Будет ли приемлема двойная новость для нового заявления?

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

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

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

и любое вмешательство этих структур. Ик!

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

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

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

Задний UPDATE склоняется к тому, чтобы отбросить ad hoc parsers. Вполне возможно, что вам не нужно беспокоиться об этом, потому что нотация DECLARE (которая действительно является встроенным SQL, а не простым SQL) не разрешается в первую очередь. Но предложение FOR UPDATE может появляться на некоторых диалектах SQL, даже если оно не указано в инструкции DECLARE, поэтому будьте осторожны.

возможно, со следующим 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>")); } 
     $ sMultiQuery = 'SHOW TABLES;  SELECT * FROM `test` ';
     $ aQueries = array ();

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

     foreach ($ aQueries как $ sQuery)
     {
         # Делай свое дело
     }

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

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

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

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

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

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

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

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

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

Полагаю, вы могли бы это разобрать. Ищите ключевые слова SELECT, DELETE, UPDATE, INSERT, EXEC и т. Д.

При анализе, если вы столкнулись с "(" приращение счетчика: nest_level ++

Если вы столкнулись с ")" декретом nest_level–

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

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

  INSERT ... SELECT .... 

Итак, для INSERT вам придется искать либо SELECT, либо VALUES …

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

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