2009-03-11 3 views
1

Вступление: я надеюсь, что для этого существует библиотека или рутина, но я не смог найти ничего подобного. Я действительно ищу направление и советы о том, с чего начать ...Разделите несколько операторов 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. Neatly закладками 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 попытки, но это, кажется, не быть достаточно мощным.

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

+0

Пункт 7 действительно делает этот медведь ... даже Oracle и SQL Server требуют определенного типа разделителем между операторами (;, GO, \, и т.д.). Это ПРЕПЯТСТВУЕТ Трудно с разделителем (например, просто подумайте обо всех местах, куда может идти SELECT, плюс у вас есть UNION и подобные заявления, чтобы конкурировать с –

+0

BTW. Студия SQL Server Management требует «GO» или «;» между несколькими заявления ... просто не для 1. Вы просите простой способ сделать что-то настолько трудное, что даже MS и Oracle не предоставляют его. –

+0

@LuckLindy: «Студия управления SQL Server требует« GO »или« GO », «между несколькими утверждениями ... просто не для 1.« Это на самом деле неправильно. Просто FYI. Откройте SQL Studio и проверьте его. – Justin

ответ

0

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

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

+0

Добавленный пункт 7. Вспомните, что я определенно не хочу ограничителя. – Justin

1

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

+0

Я проверю это. Мне нужно сделать это в коде, так что я не Конечно, это сработает, но завтра я посмотрю. – Justin

0

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

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

+1

Я недавно просто работал над SQL Parser. Первоначально я думал, как вы это делали, что он должен быть относительно прямым, но не обманывайте себя. Даже с помощью стороннего компонента SQL Parser мне все равно пришлось писать 600 строк кода, чтобы сделать довольно простой синтаксический анализ. – Craig

+1

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

0

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

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

+0

Да, я сделал. Код стал отвратительным и длинным, и я постоянно находил варианты использования, которые разбивали его, поэтому я решил, что должен быть более гибкий способ. – Justin

0

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

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

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

+0

«Не требуется ограничитель - верный путь к безумию». Я полностью не согласен. Это одна из лучших функций в MS SQL Management Studio. – Justin

+0

Если предположить, что комментарий LuckyLindy выше, даже SQL Management Studio использует описанный мной подход. Не требуя разделителей, вам потребуется написать полный парсер SQL, такой же сложный, как и сам сервер. Не делай этого. «Сохранение» пользователей «проблема» с запятой будет только больно в долгосрочной перспективе. – kquinn

+0

@kquinn - SQL Server Management Studio не выполняет _not_ требует разделителя. – Justin

-1

Вы можете разобрать его сами, я полагаю. Ищите ключевые слова ВЫБРАТЬ, DELETE, UPDATE, INSERT EXEC и т.д.

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

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

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

Вы должны будете также рассматривать дела как

INSERT ... 
SELECT .... 

Так для вставки вы бы искать либо SELECT, или VALUES ...

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

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

+0

Да, это все ловушки, которые я поймал, пытаясь написать свой собственный алгоритм. – Justin

3

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

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

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

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

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

заявления SQL может быть на одной или нескольких линий, так что я не могу обернуть на LBS/CRS

бы двойной перевод строки-для-нового-заявления быть приемлемым?

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

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

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

и любое перемещение этих структур. Ик!

+0

Все хорошие моменты, но я не хочу требовать разделителя. Это очень возможно и безопасно разобрать его, просто посмотрите @ SQL Management Studio. Я не сказал, что это будет легко. – Justin

1

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

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

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

1

возможно со следующим 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
 
    $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 
    }