Свертывание объемной базы средствами SQL




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2024-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

45 Comments

  1. w-divin

    прикольно… оч полезно и познавательно… Можно поподробней про удаление? ибо тема больная очень… база ок 120Гб, работает 24/7, так что монопольный доступ — беда большая…

    Reply
  2. barelpro

    спасибо! Подробней пока не могу, времени нет. Вкратце концепция:

    Типовой механизм платформы «Удаление помеченных объектов» имеет два недостатка: работает очень медленно и требует монопольного режима.

    Медленно потому, что:

    1. пытается сделать все и сразу для всех объектов. Хотя есть возможность ручного выбора объектов, но ты же не будешь как дятел по одному объекту выбирать каждые пять минут?

    2. пытается найти и показать все ссылки на объект (хотя справедливости ради надо отметить: в упр формах есть режим «Полное удаление» — там на это время не тратиться, но опять же сразу для всех объектов)

    Монопольный режим потому, что 1С исходит из самого пессимистичного сценария — во время проверки на удаляемость пользователь может создать ссылку на удаляемый объект, а мы его уже поставили в очередь на удаление. Могут появится битые ссылки.

    Я сделал свою обработку, в которой пришлось повторить механизм платформы на поиск ссылок. Работает он быстро, примерно секунду уходит на анализ каждого удаляемого объекта. Кроме того, я анализирую документы без проводок (т.е. проводки мы уже удалили, но документ еще не пометили на удаление, экономим время!), значит вероятность, что за 1 секунду пользователи создадут ссылку на такой документ ничтожно мала. Поэтому она работает в разделенном режиме. Как умный пылесос — ходит по квартире и никому не мешая убирает пыль.

    Reply
  3. w-divin

    да теория как раз понятна… больше интересует практика. я столкнулся с парой проблем:

    1) перекрестные ссылки (когда один объект, помеченный на удаление ссылается на другой, тоже помеченный)

    2) и из п.1 вылезает геометрическая проверка ссылок.

    т.е. если все объекты, которые ссылаются на удаляемый помечены на удаление, то первый можно удалять, но тут входим в рекурсию и зачастую возвращаемся к ссылкам на первый объект где-то на 3-9 уровнях (((

    Reply
  4. barelpro

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

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

    Reply
  5. w-divin

    (6) Спасибо — действительно был не в курсе (((

    ПыСы. переклацал все на сайте — но так и не нашел где можно пополнить руб. кошелек ((( В разделе кошелек только состояние, транзакции — только история, вывод есть, а ввода нету (((

    Reply
  6. Bienko

    (1) w-divin, на ИТС есть специальная обработка для удаления помеченных объектов. Она позволяет без проблем удалять в и без монопольного режима…

    Reply
  7. Новиков

    (8) Bienko, верно.

    Хотелось бы услышать мнение автора по этому поводу 🙂

    Reply
  8. barelpro

    О да, есть такая обработка УдалениеПомеченныхОбъектов.epf (или DeleteMarkedObjects). 🙂

    Но вы видимо не пробовали ее запускать в базе от 100Gb и с количеством помеченных объектом несколько десятков тысяч.

    Недостатки у нее те же, что встроенной в платформу (писал выше), кроме возможности удалять в разделенном режиме.

    PS. Она использует все тот же ущербный, встроенный в платформу метод ТаблицаСсылок = НайтиПоСсылкам(МассивКУдалению). Именно он работает крайне медленно. Даже если переписать обработку и подсовывать ей объекты по одному, она будет искать все ссылки на объект, на чем тратится безумное количество времени. Я этот метод переписал по своему. Я готов выложить свою обработку хоть сейчас, но хочется красиво написать. Поэтому ждите статейку 🙂

    Reply
  9. w-divin

    (8) Bienko, не успел отписать… выше уже все сказали по этому поводу…

    (10) по свертке: пробовать тока начал — пока поразворачивал тестовые базы, то да се… первые грабли.

    select * into ##tmp from _InfoRg3091 where _Period >= ‘4010-10-31 00:00:00’

    у меня не отработало:

    Сообщение 242, уровень 16, состояние 3, строка 3

    Преобразование типа данных varchar в тип данных datetime привело к выходу значения за пределы диапазона.

    Выполнение данной инструкции было прервано

    переписал на

    select * into ##tmp from _InfoRg8680 where _Period >= ‘40101031’

    смотрю дальше…

    Reply
  10. barelpro

    (11) w-divin, а что за версия SQL? Я пробовал на 2008. На более древних такой сюрприз возможен.

    Reply
  11. w-divin
  12. w-divin

    Надо будет на 2012 еще потестить — а то собираемся перелазить на 12 скуль…

    Reply
  13. Rothschild

    Остается только TRUNCATE. Он мгновенно очищает все содержимое таблицы

    я бы не стал утверждать так категорично!

    Как то раз мы попробовали таким образом «зачисить» в тестовой базе

    один кривой «распухший» регистр сведений.

    ***

    в этом регистре одни франчи вздумали в строковый ресурс неограниченной длины

    записывать некоторое строковое представление объектов довольно большого размера.

    (могли вы для этого ХранилищеЗначения использовать — придурки!)

    ***

    так вот руль скуля завис капитально — и пришлось его … убить

    Reply
  14. w-divin

    (15) Rothschild, а че — DBCC никак?

    Reply
  15. w-divin

    (8) Bienko, и да, чуть не забыл — еще в платформе есть обалденная вещь — непосредственное удаление — оно тоже работает без монопольного режима )))

    Reply
  16. w-divin

    По теме — в процедуре пересчета итогов не хватает индикации прогресса пересчета — как во всех остальных действиях )))

    Reply
  17. barelpro

    (11) w-divin,

    нда, согласен, мой косяк. Надо было дату представлять в каноническом виде ‘YYYYMMDD HH:MM:SS’. Его скуль понимает при любой языковой раскладке. Вот статья: http://www.sql.ru/faq/faq_topic.aspx?fid=109

    Сегодня исправлю и перезалью обработку

    Reply
  18. wondermaker
    Reply
  19. barelpro

    (20) wondermaker,

    спасибо за развернутый комментарий!

    Мой случай немного другой. УПП 1.2 к нашему приходу была сильно доработана, с натянутым УАТ, УПО и БИТ Финанс. Итоговое количество таблиц — 7800. в УТ 11 из всего 2800. Значит на поиск ссылок или на ту же реструктуризацию уходит больше времени. Количество записей в некоторых таблицах доходило до 50млн (ЗначенияСубконто в Регистре бухгалтерии).

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

    Да, в нашей команде присутствовал один из одинесиков-разработчиков УПП. Он так же как и вы был категорически против прямых запросов. IMHO это тонкая религиозная материя. Но в итоге должен побеждать здравый смысл.

    Reply
  20. w-divin

    (20) wondermaker, типовым конфигурациям — типовые решения… А что делать тем у кого конфигурации ну совсем не типовые? Да, нас мало, но зато мы в тельняжках!

    И я не совсем понял Ваше

    Ну и мои личные ощущения — не на столько сильно влияет «прямой SQL» на сворачивание, чтобы рисковать базой и отказываться от типовой схемы. Не на много быстрее работает удаление данных командой SQL — проверял на разных базах, на разных серверах (в том числе и почти рабочих станциях временно превращенных в «сервер» с весьма посредственными характеристиками).

    Вы обработку-то смотрели? Там прямыми запросами только удаление и сделано…

    провести дефрагментацию файлов и таблиц SQL — вот это реально даст эффект.

    особенно на SSD )))

    Reply
  21. speshuric

    Кратенько критика:

    • На небольших базах в 100 ГБ прямые запросы скорее вредят чем помогают.
    • Не используйте sysobjects, если у вас база не на 2000м SQL. Это прямая рекомендация от MS. Если у вас база на SQL Server 2000, то очень давно пора подумать о миграции на что-то более свежее (2008R2 или 2012).
    • Размер таблиц 1С можно выяснить элегантнее:
      Код
      sel ect
         t.name as table_name,
         sum(sz.used_page_count*8) used_size,
         sum(sz.reserved_page_count*8) reserved_size,
         max(sz.row_count) row_count
      fr om
         sys.dm_db_partition_stats sz
         inner join sys.tables t on t.object_id = sz.object_id
      group by t.name
      order by reserved_size desc

      Показать полностью

      И никаких курсоров и временных таблиц. Тут, правда, учитывается, что в 1С нет ни хранимых представлений, ни секционирования, ни схем отличных от dbo, а размеры индексов и данных суммированы.

    • Не описан переход от 2013 года к 4013, на самом деле есть немало баз где смещение 2000 не используется. Ну и неаккуратное преобразование строк в даты. В скриптах я бы использовал формат "{ts ‘4013-12-13 00:00:00’}"
    • У truncate/insert есть некоторая сложность, если применять не к регистрам, а к документам или справочникам. В объектных типах есть поле timestamp. Его нельзя просто так взять и вставить, нужно конвертировать.
    • Delete кусочками хоть и медленнее, чем truncate/insert, но тоже применим (и журнал остаётся в предсказуемых пределах)
    • GO не является частью синтаксиса T-SQL и ваши скрипты можно запускать только из SSMS
    Reply
  22. speshuric

    странно покорёжился скрипт. Sel ect и fr om, конечно же не должны разрываться пробелами 🙂

    Reply
  23. comol

    Только за Truncate Table админ SQL, если таковой имеется, подвесит за… уши. Далее о том что в базе могут быть ошибки, ошибки скрипта с датами и т.п. я думаю не стоит уже говорить… а уж о том что конечно данные надо разделять а не сворачивать я тем более промолчу…

    Reply
  24. Bajo
    Разумеется, перед всеми действиями по сворачиванию регистров не забывайте архивировать базу данных. Мой совет – лучше это делать средствами SQL: Tasks – Back Up. И на закладке Options не забывать устанавливать параметр “Set backup compression”: “Compress backup”. В этом случае размер архива будет почти таким же, как при выгрузке информационной базы в файл *.dt, а скорость в разы выше!

    не совсем согласен. у нас база 8 гб. бэкап с компрессией весит 1 гб, а дт-файл 170 мб

    Reply
  25. barelpro

    (26) comol,

    Олег, уважаю твое мнение, но это не голая теория, опробована на реальном проекте. Да риск есть, но кто не рискует, тот не выигрывает! 🙂

    Reply
  26. Bajo

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

    Reply
  27. Rothschild

    (27) Bajo,

    да … но ходят слухи, что из файла *.dt не всегда удается развернуть базу

    :)))

    Reply
  28. barelpro

    (24) speshuric, спасибо за критику!

    Про timestamp знаю, но я и не предлагаю применять insert к ссылочным объектам.

    За короткий скрипт определения объемов — спасибо!

    Про смещение 2000 — в сервере 1С:8.3 он уже зашит по умолчанию без возможности изменения.

    Про GO знаю, но я его и не выполняю через ODBC, моя обработка просто выдает текст скрипта, который надо запустить в SSMS. После каждого GO завершается транзакция и выдается комментарий, за которым я предлагаю наблюдать.

    Ну а в целом — приятно поговорить с человеком в теме! ))))

    Reply
  29. barelpro

    (27) Bajo,

    да, SQL жмет не сильно, раз в 5. Но делает он это быстро. А в нашем деле время — деньги!

    Reply
  30. w-divin

    (30) Rothschild, а иногда удается развернуть битую )))

    (27) и почему разработчики БСП и типовых конфигураций при попытке сделать бекап клиент-серверной базы пишут «пользуйтесь средствами СУБД»? они наверное не в теме (((

    Reply
  31. yuraos

    (25) speshuric,

    да — есть такой косяк в движке сайта





    хоть по 1с -ному пиши:

    ВЫБРАТЬ ***
    ИЗ ***
    
    Reply
  32. haggart

    Первый вопрос. Хоть у кого-то получилось из тех кто скачал?

    Второй вопрос. После того как Recovery model установлен в Simple и базу свернули, опять можно возвращаться к предыдущему варианту Recovery model? Но тогда будут ли работать Tasks?

    Reply
  33. barelpro

    (35) haggart,

    а что конкретно не получилось?

    Reply
  34. Alex1Cnic

    А картинка то в тему!!! 🙂

    Reply
  35. ASchekachev

    Может кому пригодится

    Reply
  36. barelpro

    Антон, спасибо, это альтернативный способ вычисления размеров таблиц через запрос к sysobjects

    а у меня используется хранимая процедура sp_spaceused

    преимущество моего метода в том, что я кроме Total_Rows, Data_Size и Index_Size вижу еще и Unused_space и полный размер Total_Table_Size

    Reply
  37. V.Nikonov

    Лично у меня несколько другой подход к выбору приоритетов по зачистке Регистров. Предпочитаю ориентироваться не столько на размер таблиц (Записей, Занимаемое место), сколько на ключевой характер остатков (влияние на логику оперативного проведения). В частности есть большое количество регистров носящих вспомогательный характер (для ускорения отчетов), а часть регистров влияет на логику поведения. Соответственно Регистры отвечающие за Взаиморасчеты, Товарные остатки, Резервы и некоторые другие надо зачищать в первую очередь.

    После зачистки Ключевых регистров можно даже пользователей в программу пускать продолжая зачистку штатными средствами. При этом работоспособность (кроме отдельных видов отчетов) будет полная, хотя и будет наблюдаться увеличенная нагрузка на Сервера (сниженная производительность оперативного потока).

    Reply
  38. StaticUnsafe

    Что на фото? 😉

    Reply
  39. barelpro

    на фото та самая матчасть, которую надо курить )

    Reply
  40. evgeniyk1@yandex.ru

    Приветствую. Столкнулся с странной ситуацией. После подобной обработки размер таблицы итогов некоторых регистров накопления увеличивается.

    Есть мысли почему и как избежать? А то получается после свертки этого регистра объем вырос на 30%.

    Пример
    Reply
  41. barelpro

    возможно у вас не установлен параметр SQL-базы recovery model = simple

    Reply
  42. evgeniyk1@yandex.ru

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

    Спасибо. Разобрались.
    Reply
  43. Tarlich

    Не подходит к БП 2.0 -(( зря потрачены мани…

    Reply
  44. acanta

    Что Вы посоветуете, вот эту обработку вместо пункта 4 применять с RabbitMQ или достаточно РИБ?

    Reply
  45. barelpro

    (47) Честно говоря не думал об этом ) Смотря для какого случая. Обработка средствами SQL не оставлет никаких следов и никак не отслеживается средствами 1С. Ее главные плюс — скорость удаления устаревшей информации.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *