Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Не получили письмо с кодом активации?

Официальный форум Simple-Scada.

Автор Тема: Запись данных в таблицу MySQL  (Прочитано 6582 раз)

elek72

  • Новичок
  • *
  • Сообщений: 29
    • Просмотр профиля
Запись данных в таблицу MySQL
« : 02 Июня 2018, 13:39:38 »
День добрый.
Необходимо перетащить некоторые данные из текстового файла в таблицу базы данных MySQL(не менее 5000 строк в таблицу).
Следующая процедура для теста - в цикле вставляет в таблицу записи и успешно завершается.
Процедура завершена, а запись в базу данных на самом деле продолжается - как отследить этот процесс ?
Пробовал через скрипт по событию "Выполнен SQL запрос" выводить в поле на экран текущий aQuery - отображает последний запрос, а по факту запись не выполнена.
Запись в БД продолжается вплоть до остановки проекта - SCADA кэширует запросы ?
Закрываем скаду, переходим в Workbench и видим что записаны не все данные.
Подскажите как надо правильно делать.
 
Код: (delphi)
var
  aQuery, text: string;
  i: integer;
begin
  aQuery := 'CREATE TABLE IF NOT EXISTS '+ 'Test_Data' +' (' +
            '`ID` INT UNSIGNED NOT NULL AUTO_INCREMENT,' +
            '`Test_string` CHAR(32),'+
            'PRIMARY KEY (`ID`)' +
            ') ENGINE = InnoDB DEFAULT CHARACTER SET = cp1251;';
  RunSQL(aQuery, nil, 0);

  for i := 1 to 10000 do
  begin
      text := QuotedStr('Цикл => '+inttostr(i));
      aQuery := 'INSERT INTO `Test_Data` ' +
                '(`Test_string`) VALUES ' +
                '('+ text +');';
      RunSQL(aQuery, nil, 1);
  end;
end.
« Изменён: 02 Июня 2018, 13:48:31 от Simple-Scada »

Simple-Scada

  • Администратор
  • *****
  • Сообщений: 3210
    • Просмотр профиля
    • Simple-Scada
Re: Запись данных в таблицу MySQL
« Ответ #1 : 02 Июня 2018, 15:11:32 »
Здравствуйте.

Процедура RunSQL просто добавляет запрос в очередь запросов. Затем запросы последовательно берутся из очереди и отправляются на выполнение в СУБД. А выполнять запросы СУБД может сколько угодно, скорость выполнения зависит от конфигурации ПК, конфигурации СУБД, свободной оперативной памяти, сложности запроса и т.д.

В приведенном коде Вы посылаете отдельный запрос на вставку каждой строки и не удивительно, что эти запросы долго выполняются. Вместо этого нужно группировать строки в один запрос используя соответствующий синтаксис заложенный MySQL, например:
Код: (delphi)
aQuery := 'INSERT INTO `tbl_name` (a, b, c) VALUES(1, 2, 3), (4, 5, 6), (7, 8, 9);';
, где tbl_name - имя таблицы в которую идёт вставка, a,b,c - колонки. Далее перечисление значений, которые нужно вставить. В данном примере производится вставка сразу трёх "строк". Подробнее про синтаксис можно прочесть на официальном сайте MySQL. Также нужно учитывать, что есть ограничение на длину запроса и один запрос на все 10000 записей может оказаться слишком длинным, поэтому лучше группировать не все строки сразу, а группами, например по 500 штук.

Цитировать
Подскажите как надо правильно делать.
А зачем выполнять вставку данных из файла в БД через скаду? Это очень нестандартное решение для заполнения БД и нестандартное использование скады. Может быть лучше воспользоваться функцией импорта через MySQLWorkbench (см. вложение)?
« Изменён: 02 Июня 2018, 15:12:36 от Simple-Scada »

elek72

  • Новичок
  • *
  • Сообщений: 29
    • Просмотр профиля
Re: Запись данных в таблицу MySQL
« Ответ #2 : 02 Июня 2018, 16:58:43 »
Исходный "текстовый" файл содержит информацию по раскрою элементов (экспорт из Cadwork), скрипт разбирает файл и получает рабочие параметры(координаты операции, тип операции) - например в проекте содержащем 250 деталей  получается 7200 операций(строк в БД), каждая операция имеет 22 параметра, в исходном файле 15762 строки. Процесс (скрипт) разборки файла занимает 2-3 сек.
Меня в принципе не интересует как долго СУБД выполняет запросы, конвертация проекта в БД не каждый день.

Получается скрипт по событию "Выполнен SQL запрос" - на самом деле "SQL запрос помещен в очередь".

Технически возможно ли создать настоящее событие "Выполнен SQL запрос" - т.е. ушел в СУБД, что бы можно было реально отследить процесс?

По цели использования SCADA:
В качестве HMI - индикация текущих параметров, событий обрабатывающего центра;
Отправка рабочего задания в ПЛК (из СУБД).
Вся логика управления-работы, безопасность только в ПЛК, с HMI только запросы на переход в какой либо режим.
Использую пока time Demo, по окончании разработки проекта купим соответствующую лицензию, если конечно не придется перейти на другое устройство.
« Изменён: 02 Июня 2018, 20:13:17 от elek72 »

pan2000

  • Постоялец
  • ***
  • Сообщений: 225
    • Просмотр профиля
Re: Запись данных в таблицу MySQL
« Ответ #3 : 03 Июня 2018, 10:54:41 »
Событие "Выполнен SQL запрос" это действительно признак завершения выполнения запроса и для его обработки требуется скрипт соответствующего типа.

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

Для выполнения запросов "поодиночке" Ваш пример можно изменить так:

1. Начало процесса:
Код: (delphi)
var
  aQuery: string;
begin
  aQuery := 'CREATE TABLE IF NOT EXISTS '+ 'Test_Data' +' (' +
            '`ID` INT UNSIGNED NOT NULL AUTO_INCREMENT,' +
            '`Test_string` CHAR(32),'+
            'PRIMARY KEY (`ID`)' +
            ') ENGINE = InnoDB DEFAULT CHARACTER SET = cp1251;';
  RunSQL(aQuery, nil, 1);
end.

2. Выполняемый в цикле скрипт типа "Выполнен SQL запрос":
Код: (delphi)
var
  aQuery, text: string;
  i: integer;
begin
  i := DataSet.Tag;
  if i >= 10000 then exit;       // выход из цикла
  text := QuotedStr('Цикл => '+inttostr(i));
  aQuery := 'INSERT INTO `Test_Data` ' +
            '(`Test_string`) VALUES ' +
            '('+ text +');';
  RunSQL(aQuery, nil, i + 1);
  Text1.Text := text; // индикация хода процесса
end.
Пример во вложении.

P.S. В Вашей задаче разумно использовать на одну операцию один SQL запрос и записывать по 22 параметра.

« Изменён: 03 Июня 2018, 10:57:58 от Simple-Scada »

Simple-Scada

  • Администратор
  • *****
  • Сообщений: 3210
    • Просмотр профиля
    • Simple-Scada
Re: Запись данных в таблицу MySQL
« Ответ #4 : 03 Июня 2018, 11:45:26 »
Цитировать
Получается скрипт по событию "Выполнен SQL запрос" - на самом деле "SQL запрос помещен в очередь".
Нет, событие "Выполнен SQL запрос" вызывается когда SQL-запрос выполнен полностью и СУБД вернула ответ о том, что запрос выполнен. Если бы это было добавление в очередь, то мы бы так и назвали событие. В целом все существующие события сейчас названы именно так, как они работают.

Цитировать
Технически возможно ли создать настоящее событие "Выполнен SQL запрос" - т.е. ушел в СУБД, что бы можно было реально отследить процесс?
Да, событие "Выполнен SQL запрос" это и есть настоящее выполнение SQL-запроса.

Цитировать
Пробовал через скрипт по событию "Выполнен SQL запрос" выводить в поле на экран текущий aQuery - отображает последний запрос, а по факту запись не выполнена.
Вывести SQL-код текущего SQL-запроса (текущий aQuery) из скрипта с событием "Выполнен SQL запрос" - невозможно. Видимо Вы просто вывели на экран код запроса отправленного в очередь и конечно после выполнения цикла он будет равен коду последнего запроса. Если Вы хотите увидеть прогресс выполнения запросов, то нужно выводить информацию на экран из скрипта "Выполнен SQL запрос", как выше написал pan2000. В таком скрипте можно получить только Tag и Sender-объект присвоенные запросу во время отправки в очередь. Например:

Скрипт запускает выполнение 100 запросов, каждый запрос помечается уникальным тегом:
Код: (delphi)
var
  I: Integer;
  aQuery: string;
begin
  for I := 0 to 100 do
  begin
    aQuery := 'INSERT INTO `test`(`value`) VALUES (' + IntToStr(I) + ');';
    RunSQL(aQuery, nil, I); // в качестве тега запроса передаем значение переменной I
  end;
end.

Скрипт "Выполнен SQL-запрос":
Код: (delphi)
begin
  Text1.Text := IntToStr(DataSet.Tag); // выводим Tag выполненного SQL-запроса в объект Text1
end.

Вариант, предложенный pan2000 тоже рабочий, но учитывайте что он будет работать медленнее, т.к. новый запрос будет добавляться в очередь только после выполнения предыдущего. Поэтому все-таки лучше отправлять в очередь сразу все запросы.
« Изменён: 03 Июня 2018, 11:49:32 от Simple-Scada »

elek72

  • Новичок
  • *
  • Сообщений: 29
    • Просмотр профиля
Re: Запись данных в таблицу MySQL
« Ответ #5 : 03 Июня 2018, 12:13:07 »
Всем спасибо за ваши советы.
По рекомендации Simple-Scada сгруппировал набор данных и теперь запись в СУБД одновременно заканчивается с декодированием файла, т.е. сразу же открываю Workbench и вижу что все данные записаны.
Сгруппировал по 20 деталей (в каждой разное количество процессов) - получается около 300 строк (31 столбец не считая ID).

upd: Добавил для каждого запроса уникальный тег + флаг соответствующей операции, в результате теперь можно отслеживать прогресс, а группировку записи деталей уменьшил пока до 2 чтобы было время наблюдать прогрессбар (около 10 сек.):

Скрипт "Выполнен SQL-запрос":
Код: (delphi)
var
i: Integer;
begin
  i := DataSet.Tag;
  if flag_import_to_db.Value then                                  // соответствующий флаг
  begin
     progress_DB.Value := int ((i/count_Query_Insert.Value)*100);      //  шкала прогрессбар в %
     if i = count_Query_Insert.Value then
     begin
       flag_import_to_db.Value := false;
       Status_import.FlashColor := clNone;
       Status_import.Color := clDarkGreen;
       Status_import.Text := 'Обработка завершена.';
       Button_ImportToDB.Enabled := true;
     end;
  end;
end.     
« Изменён: 03 Июня 2018, 19:27:47 от elek72 »