Форум программистов
 
О проблемах, например, с регистрацией пишите сюда - alarforum@yandex.ru, проверяйте папку спам! Обязательно пройдите активизацию e-mail, а тут можно восстановить пароль.

Вернуться   Форум программистов > Delphi программирование > БД в Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу здесь за 20 тыс руб в месяц! alarforum@yandex.ru


Ответ
 
Опции темы
Старый 04.09.2020, 12:42   #1
iskurt
Форумчанин
 
Регистрация: 02.06.2009
Сообщений: 214
По умолчанию MySQL Delphi *.csv

Всем привет.
Впервые с толкнулся с задачей, где нужно перебрать csv файл и загнать в БД.
Проблема оказалась в том, что периодически пользователь должен скачивать файл *.csv с количеством строк больше миллиона!!! (Мать его так...) Пробовали работать в эксель, но эксель мах 1024 тыс строк. Power Pivot намного быстрее, но не все нужные функции поддерживает. Хотел загнать в БД и выводить отчеты нужные.
Парсинг сделал, обрабатывает с нормальной скоростью...
Но при попытке загонять в БД ловлю огромезные тормоза... примерно 20000 строк в минуту, я даже не стал ждать до окончания.
Возможно, даже скорее всего я выбрал не правильный алгоритм.
Вопрос: Может ли кто подсказать, каким алгоритмом загонять данные в таблицу?
Код:

var
  FImport: TFImport;
  slist: TStringList;
  Data: array of array of string;

procedure TFImport.FillDBG(Raw: Integer);
begin
  with ADOQuery1 do
  begin
    Close;
    SQL.Clear;
    SQL.Add('INSERT INTO operacii SET date_oper = :date_oper, date_oform = :date_oform');
    Parameters.ParamByName('date_oper').Value := Data[1, Raw];
    Parameters.ParamByName('date_oform').Value := Data[2, Raw];
    ExecSQL;
  end;
end;

procedure TFImport.LoadCSVFile(st: TStringList; sep: char);
var // перечитываем файл csv
  s1, s2: String;
  i, j, k, k1: Integer;
  q: Boolean;
begin
  i := 0;
  SetLength(Data, 39, st.Count);
  q := False;
  for k := 1 to st.Count - 1 do
  begin
    inc(i);
    j := 0;
    s1 := st[k];
    while pos(sep, s1) <> 0 do // если найден сепаратор
    begin
      s2 := copy(s1, 2, pos(sep, s1) - 3);
      inc(j);
      delete(s1, 1, pos(sep, s1));
      Data[j, i - 1] := s2;
      q := True;
    end;
    if pos(sep, s1) = 0 then // если больше не найден
    begin
      j := j + 1;
      Data[j, i - 1] := s1;
      q := True;
    end;

    if q then
    begin
      FillDBG(k);
    end;
  end;
Думаю я выбрал всё же не верный подход, подскажите как ускорить?
В кратце об алгоритме:
Перечитываем файл и загоняем всё в двумерный массив (происходит быстро). Затем, построчно, загоняю в БД (каждая строка - отдельный запрос ExecSQL).
"Люди, добрый, помогите кто чем может..."
iskurt вне форума Ответить с цитированием
Старый 07.09.2020, 10:46   #2
evg_m
Старожил
 
Регистрация: 20.04.2008
Сообщений: 5,103
По умолчанию

Цитата:
Код:
 while pos(sep, s1) <> 0 do // если найден сепаратор
    begin
      s2 := copy(s1, 2, pos(sep, s1) - 3);
      inc(j);
      delete(s1, 1, pos(sep, s1));
      Data[j, i - 1] := s2;
      q := True;
    end;
    if pos(sep, s1) = 0 then // если больше не найден
...
pos delete copy рекомендации отсюда
https://www.programmersforum.ru/show...59&postcount=4

Цитата:
Код:
  with ADOQuery1 do
  begin
    Close;
    SQL.Clear;
    SQL.Add('INSERT INTO operacii SET date_oper = :date_oper, date_oform = :date_oform');
    Parameters.ParamByName('date_oper').Value := Data[1, Raw];
    Parameters.ParamByName('date_oform').Value := Data[2, Raw];
    ExecSQL;
  end;
Код:
    ADOQuery1.SQL.Clear;
    ADOQuery1.SQL.Add('INSERT INTO operacii SET date_oper = :date_oper, date_oform = :date_oform');

for k:=0 to st.count-1 do begin
   q:=false;
   .....
   if q then begin   
    raw:=k;
    ADOQuery1.Parameters.ParamByName('date_oper').Value := Data[1, Raw];
    ADOQuery1.Parameters.ParamByName('date_oform').Value := Data[2, Raw];
    ADOQuery1.ExecSQL;
  end;

end;
программа — запись алгоритма на языке понятном транслятору

Последний раз редактировалось evg_m; 07.09.2020 в 10:50.
evg_m на форуме Ответить с цитированием
Старый 07.09.2020, 10:49   #3
iskurt
Форумчанин
 
Регистрация: 02.06.2009
Сообщений: 214
По умолчанию

Ага, логику увидел, спс, пробую
iskurt вне форума Ответить с цитированием
Старый 07.09.2020, 10:57   #4
evg_m
Старожил
 
Регистрация: 20.04.2008
Сообщений: 5,103
По умолчанию

Код:
Цитата:
for k:=0 to st.count-1 do begin q:=false; ..... if q then begin raw:=k; ADOQuery1.Parameters.ParamByName('date_oper').Value := Data[1, Raw]; ADOQuery1.Parameters.ParamByName('date_oform').Value := Data[2, Raw]; ADOQuery1.ExecSQL; end; end;
а теперь вообще говоря и такой большой массив
Цитата:
Код:
  SetLength(Data, 39, st.Count); // st.count >=1000000
не нужен. достаточно помнить данные одной строки.
программа — запись алгоритма на языке понятном транслятору
evg_m на форуме Ответить с цитированием
Старый 07.09.2020, 12:22   #5
iskurt
Форумчанин
 
Регистрация: 02.06.2009
Сообщений: 214
По умолчанию

хм, в общем на 100000 строк скорость увеличилась на 1 минуту (ранее операция занимала 14,5 мин, теперь 13,5 мин)
iskurt вне форума Ответить с цитированием
Старый 07.09.2020, 14:04   #6
evg_m
Старожил
 
Регистрация: 20.04.2008
Сообщений: 5,103
По умолчанию

а что именно тормозит?
MySQL или разбор ?
что будет если исключить
Код:
///    ADOQuery1.ExecSQL;
программа — запись алгоритма на языке понятном транслятору
evg_m на форуме Ответить с цитированием
Старый 07.09.2020, 14:19   #7
iskurt
Форумчанин
 
Регистрация: 02.06.2009
Сообщений: 214
По умолчанию

тормозит именно MySQL.
Разбор работает быстро - Результат примерно 1-2 сек 99900 строк
iskurt вне форума Ответить с цитированием
Старый 07.09.2020, 14:54   #8
iskurt
Форумчанин
 
Регистрация: 02.06.2009
Сообщений: 214
По умолчанию

Код:
  t1 := DateTimeToStr(Now());
  ADOQuery1.Close;
  ADOQuery1.SQL.Clear;
  ADOQuery1.SQL.Add
    ('INSERT INTO operacii SET date_oper = :date_oper, date_oform = :date_oform');
  for i := 0 to st.Count - 2 do
    with ADOQuery1 do
    begin
      Parameters.ParamByName('date_oper').Value := Data[1, i];
      Parameters.ParamByName('date_oform').Value := Data[2, i];
      // ExecSQL;
    end;
  t2 := DateTimeToStr(Now());
  ShowMessage(t1 + '----' + t2);
результат Снимок.PNG
iskurt вне форума Ответить с цитированием
Старый 07.09.2020, 15:05   #9
iskurt
Форумчанин
 
Регистрация: 02.06.2009
Сообщений: 214
По умолчанию

Вот что странно, SSD диск при этом нагружен очень сильно
Снимок2.PNG

Получается что это самое слабое звено?
Снимок3.PNG
iskurt вне форума Ответить с цитированием
Старый 09.09.2020, 16:24   #10
iskurt
Форумчанин
 
Регистрация: 02.06.2009
Сообщений: 214
По умолчанию

Снимок321.PNG
Ачуметь, на MS SQL Server 2016 - 21 гребаная секунда!!!

Подозреваю, помогло то, что я начальный размер базы указал 512мб с расширением в 64мб.
Как считаете, могло это повлиять на скорость?
Если могло, то как задать начальный размер MySQL базы с указанием авто увеличением размера?
iskurt вне форума Ответить с цитированием
Ответ

Здесь нужно купить рекламу за 20 тыс руб в месяц! ) пишите сюда - alarforum@yandex.ru
Без учёта ботов - 20000 человек в день, 350000 в месяц.

Опции темы


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
[DELPHI + MySQL на хостинге] Возможен ли обмен сообщениями между компьютерами через MySQL на сайте? garuna БД в Delphi 5 21.08.2019 19:50
mySQL+DELPHI от и до shuhermayer SQL, базы данных 21 20.10.2011 20:22
Mysql+Delphi 2010 Использую libmysql.dll + mysql.pas не могу инф сохранить в базу kGpROGER БД в Delphi 7 30.12.2010 18:27
Delphi+MySQL prod87 БД в Delphi 2 18.08.2010 09:09


Проекты отопления, пеллетные котлы, бойлеры, радиаторы
интернет магазин respective.ru
Пеллетный котёл Emtas
котлы EMTAS
Здесь нужно купить рекламу за 7 тыс руб в месяц! )
пишите сюда - alarforum@yandex.ru
ИКС 840