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




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

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

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

<?php // Полная загрузка сервисных книжек, создан 2026-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 не будет опубликован. Обязательные поля помечены *