Вступление: я надеюсь, что для этого существует библиотека или рутина, но я не смог найти ничего подобного. Я действительно ищу направление и совет, с чего начать …
Вот ситуация: у меня есть блок команд SQL, идущий как обычный текст. Это могут быть команды SQL или несколько. Мне нужен способ разбить несколько SQL-команд, чтобы я мог запускать их по одному. Microsoft SQL Management Studio делает это поведение из коробки.
Я пытаюсь добавить эту функциональность в приложение PHP5 / MySQL5, запущенное на Apache (Debian).
Некоторые важные моменты:
Вот пример блока 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, вы должны просто потребовать точку с запятой. Я не думаю, что в этом есть что-то «нераскрытое».