Как мне разобрать и выполнить все операторы SQL отдельно в файле?

0

Скажем, у меня есть.SQL файл, который потенциально может содержать оператор ANY типа, будь то CREATE, UPDATE, DELETE или даже весь дамп базы данных. Идея состоит в том, чтобы проанализировать содержимое этого файла, чтобы операторы отправлялись в двигатель отдельно, а не выполняли весь фрагмент.

Сначала я попытался использовать регулярные выражения и разделился ; , но проблема в том, что он ломается, когда он сталкивается с процедурой или функцией из-за синтаксиса. Например...

DELIMITER ;;
CREATE DEFINER='root'@'localhost' FUNCTION 'calcCost'(cost FLOAT, price FLOAT) RETURNS decimal(9,2)
BEGIN
  DECLARE profit DECIMAL(9,2);
  SET profit = price-cost;
  RETURN profit;
END ;;
DELIMITER ;

Разделение на ; здесь это преобразует это в 8 разных утверждений, тогда как мне нужно, чтобы он оставался одним. Кстати, символ, используемый для ключевого слова DELIMITER может измениться.

Например:

DELIMITER $$
CREATE DEFINER='root'@'localhost' FUNCTION 'calcCost'(cost FLOAT, price FLOAT) RETURNS decimal(9,2)
BEGIN
  DECLARE profit DECIMAL(9,2);
  SET profit = price-cost;
  RETURN profit;
END $$
DELIMITER ;

Будет ли это выполнимо с помощью регулярных выражений, и если да, то как мне это сделать? Что выражение регулярного выражения? Явный тег Java, потому что я ожидаю ответы, основанные на Java regex engine и утилитах.

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

  • 0
    К вашему сведению: CREATE, UPDATE и DELETE не являются запросами . Они SQL заявления. Оператор SELECT является запросом .
  • 0
    @Andreas Андреас отредактировал. Спасибо.
Теги:

3 ответа

0
Лучший ответ

Можно ли это сделать, используя регулярные выражения?

Нет.

Как мне это сделать?

Как вы заметили себя, разделитель изменчив. Поэтому вам нужно найти команду DELIMITER.

Кроме того, вы можете полагаться на файлы SQL, подобные этому, чтобы поместить только один оператор в строку, т.е. Вам нужно только искать разделитель в конце строк.

Так:

  • Установить начальный delimiter = ";" , и создать пустой буфер (StringBuilder)
  • Прочитайте файл по одной строке за раз:
    • Если буфер пуст, а строка - команда DELIMITER:
      • Обновить delimiter
      • цикл continue
    • Добавить строку в буфер, включая ограничитель строки
      Помните, readLine() удаляет терминатор строки
    • Если строка заканчивается разделителем:
      • Удалить разделитель с конца буфера
      • Выполнять команду в буфере
      • Очистить буфер
  • Конец цикла
  • 0
    Я думаю, что могу с этим поработать. Я уже нахожусь, поэтому не могу сейчас проверить, но я посмотрю, что я могу сделать. Если бы вы могли предоставить пример кода, я был бы признателен, если нет, то это тоже хорошо.
  • 0
    Перечитывая ваш ответ, я не могу понять, что вы имели в виду. Also, you can rely of SQL files like that to only put one statement per line, . Означает ли это, что ваш метод не будет работать для операторов, которые занимают более одной строки? Например: CREATE TABLE и большинство CREATE s? Потому что файлы, которые передаются в мою программу, должны разрешать оператор CREATE TABLE который занимает несколько строк. (Большинство дамп-приложений БД, таких как mysqldump форматируют файлы таким образом), и моя программа должна быть совместима с ними.
Показать ещё 2 комментария
0

Это работающая (насколько я проверял) реализация ответа Андреаса. Он делает именно то, что я хотел. Это должно быть совместимо с типом и форматом скриптов, создаваемых большинством баз данных.

try (BufferedReader reader = new BufferedReader(new FileReader(new File(System.getProperty("user.home") + "/Desktop/myscript.sql")))) {
    String line;
    String delimiter = ";";
    StringBuilder buffer = new StringBuilder();
    while ((line = reader.readLine()) != null) {
        if (buffer.length() == 0 && line.startsWith("DELIMITER")) {
            delimiter = line.split(" ")[1];
            continue;
        }
        if (line.startsWith("--") || line.isEmpty()) {
            continue;
        }
        buffer.append(line.replaceFirst("\\h+$", "")).append("\n");
        if (line.endsWith(delimiter)) {
            String statement = buffer.toString().replace(delimiter, "");
            System.out.println("{\n" + statement + "}"); //print the statement to console for debugging purposes
            //TODO execute the statement
            buffer.setLength(0);
        }
    }
} catch (IOException e) {
    //TODO handle IOException
}
  • 0
    Код не будет выполнен, если строка с разделителем имеет пробел после разделителя. Может быть хорошо просто обрезать все строки конечных пробелов, например, `buffer.append (line.replaceFirst (" \\ h + $ "," ")). Append (" \ n ")`
  • 0
    Я понимаю, что вы имеете в виду =). Но не будет buffer.append(line.trim()).append("\n"); быть более читабельным?
Показать ещё 5 комментариев
0

Я делаю что-то подобное, чтобы читать SQL файлы из файла. (Это может быть или не быть сложным с регулярным выражением, но мое не очень глубокое знание регулярного выражения заставило меня сделать это так)

Резюме:

  • Создайте Map<String, String> которая хранит имя запроса и фактический SQL-запрос.

  • Заполнение карты заполняется, когда я загружаю этот DAO, чтобы все запросы, связанные с этим DAO, были на карте.

  • Индивидуальные запросы находятся в файле SQL.

Пример кода DAO:

    private static final Map<String, String> SQL_MAP;

private static final String GET_PERSON_USING_ID;

static
{
    try
    {
        SQL_MAP = Util.readConfigureMap(<MyCurrentClass>.class, "<sql_file>");
    }
    catch (IOException iE)
    {
        throw new RuntimeException("Runtime Exception: ", iE);
    }

    GET_PERSON_USING_ID                                      = SQL_MAP.get("get_person_using_id");
}

Пример кода Util:

public static final String
    CONFIG_KEY_START = "---[",
    CONFIG_KEY_END = "]";

public static Map<String, String> readToMap(Class iClass, String iFileName)
    throws IOException
{
    final InputStream vResourceAsStream = iClass.getResourceAsStream(iFileName);

    try (Scanner vScanner = new Scanner(vResourceAsStream))
    {
        Map<String, String>
            vMap = new LinkedHashMap<>();

        String
            vCurrentKey = null;

        StringBuilder
            vValueBuffer = null;

        boolean
            vSeeFirst = false;

        while (vScanner.hasNextLine())
        {
            String
                vLine = vScanner.nextLine();

            if(vLine == null)
            {
                continue;
            }

            vLine = vLine.trim();

            if(vLine.isEmpty())
            {
                continue;
            }

            if(vLine.startsWith(CONFIG_KEY_START))
            {
                if(!vSeeFirst)
                {
                    vSeeFirst = true;
                }

                if(vCurrentKey != null)
                {
                    vMap.put(vCurrentKey, vValueBuffer == null ? null : vValueBuffer.toString());
                    vValueBuffer = null;
                }

                vCurrentKey = vLine.substring(CONFIG_KEY_START.length());
                if(vCurrentKey.endsWith(CONFIG_KEY_END))
                {
                    vCurrentKey = vCurrentKey.substring(0, vCurrentKey.length() - CONFIG_KEY_END.length());
                }

                continue;
            }

            if(!vSeeFirst)
            {
                continue;
            }

            if(vValueBuffer == null)
            {
                vValueBuffer = new StringBuilder();
            }
            else
            {
                vValueBuffer.append('\n');
            }
            vValueBuffer.append(vLine);
        }

        if(vCurrentKey != null)
        {
            vMap.put(vCurrentKey, vValueBuffer == null ? null : vValueBuffer.toString());
        }

        return vMap;
    }
}

Пример файла sql

---[get_person_using_id]
select a.name from person a where id = ?
  • 0
    SQL в файле является динамическим. Я должен загрузить и прочитать как есть, я не могу ничего добавить к нему заранее.
  • 0
    Динамический? Вы имеете в виду, что содержимое файла SQL изменяется во время выполнения программы или файл SQL содержит несколько запросов другого типа? потому что ваш вопрос не уточняет о бывшем.
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню