<?php // Полная загрузка сервисных книжек, создан 2025-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='\
select 1
while(@@ROWCOUNT>0)
DELETE TOP(500) [_AccumRg1234] WHERE <…>;
Ты был близок!
Если все то транкейт.
Если мало оставить, то копируй малую часть во времянку, транкейти, и копируй назад.
автор ни слова не сказал о кластером индексе либо о дополнительном индексировании.
(2) Я прошу прощения, но я не понял Вашего комментария. У Вас какой-то вопрос, который Вы решили не задавать? Или у Вас есть уточняющая публикацию информация, которой Вы решили не делиться?
По основному тексту статьи с индексами делать ничего не надо — свёртка выполняется «на бою». С таблицей работают пользователи. Фрагментация индекса неизбежна. Для этого существуют регламентные задания и прочее. Можно ещё поговорить о логах SQL Server и других сопутствующих темах …
По поводу варианта с использованием TRUNCATE, да — там есть такая тема. Индексы на таблице save создавать вообще не нужно. На основной таблице, как вариант, можно их дропнуть и создать потом заново. Не стал об этом писать, так как основной мотив статьи — свёртка «на бою» параллельно основной работе пользователей.
(3) ээ-э.. индексы?
…индексы фрагментируются и разок другой можно их переиндексировать руками, а потом лучше воткнуть в обслуживание.
…запрос не продолжается, а всякий раз начинается с начала (но это весчь очевидная)
Вот что действительно никто не сказал, так это режим работы сервера при удаление большого объема флуда из таблицы — можно потребовать максимальный приоритет и не уступать место другим потокам (типа крут бесконечно), тогда в сос уходить не будет (хотя это вроде тоже весчь очевидная)
так же как и отбирать , так и удалять записи таблицы быстрее всего по кластерному индексу.
поэтому все рекомендации относятся для случая , когда удаление записей нужно сделать
по условию, не соответствующему кластерному индексу.
для случая массовой го удаления записей из больших таблиц и удобно создать дополнительный индекс по условию удаления. после удаления записей индекс удалить.
если удаляется меньше половины записей, то этот способ быстрее, чем способ с транкетом.
(5) Идея понятна. Спасибо за дополнение.
Честно говоря, мне даже в голову не пришло, что что-то можно удалять не по индексу =)
Кстати сказать, пример в публикации использует отбор по полю _Period, которое является первым полем кластерного индекса для регистров накопления. В данном случае кластерный индекс будет использован.
Конструкцию
INSERT [_AccumRg1234_Save]
SELECT * FROM [_AccumRg1234] WHERE [_Period] >= @Period;
можно заменить на
SELECT *
INTO [_AccumRg1234_Save]
FROM [_AccumRg1234] WHERE [_Period] >= @Period;
которая работает, как будто бы, быстрее.
(7) Зачем гадать что быстрее ? Планы запросов в студию …
(8)
С чего вы взяли, что я гадаю? Я не гадаю,я читаю обзоры .
Даже не целиком обзоры, а лишь ключевые фразы.
Пример ключевой фразы «As we can see the SELECT…INTO was considerably faster 489ms compared to 3241ms.» ))
(9) Извините, но Вы написали: «которая работает, как будто бы, быстрее». Это выглядит как неуверенность в своих словах.
Эта ссылка и объяснения показались мне более понятными и обстоятельными:http://stackoverflow.com/questions/6947983/insert-into-vs-select-into . Можно сказать, что Вы правы … частично, так как таблица [_AccumRg1234_Save] в моём примере не является временной. Если использовать временную таблицу, как Вы предлагаете, то нужно весь код удаления в обязательном порядке заключить в одну транзакцию между BEGIN TRANSACTION и COMMIT TRANSACTION. В примере этого нет, и это не просто так. Если выполнение программы вдруг «упадёт» после TRUNCATE, то в моём примере данные останутся в таблице [_AccumRg1234_Save] и не будут потеряны.
Наверное, можно оптимизировать код и убрать 1 лишний цикл:
DECLARE @RowsToDelete int = 4000;
DECLARE @RowsAffected int = @RowsToDelete;
WHILE @RowsAffected = @RowsToDelete
…
…
(11) Простите, но я как-то с утра не могу понять где лишний цикл ? Итерация, наверное, точнее сказать …
Если @RowsAffected > 0 но < @RowsToDelete, следущая итерация удалит нулевое кол-во записей.
(13) Удаляем 5000 записей.
1. итерация: удалили 4000, осталась 1000.
2. итерация: удалили 1000, осталось 0.
3. итерация: @RowsAffected = 1000, удаляем 0 записей …
Вы правы …
Однако, а что если, пока мы удаляем записи, другая транзакция их туда добавляет ?
При определённых условиях этот цикл может быть вечным … =)
(14) а при WHILE @RowsAffected > 0 он будет вечным + 1
🙂
(15) Чтобы расставить все точки над i:
при WHILE @RowsAffected = @RowsToDelete цикл не будет вечным и после его выполнения в базе могут остаться записи, которые подходят под условие удаления.
Мой пример был взят из боевой базы. Первая версия была вообще такая: WHILE EXISTS(SEL ECT 1 FR OM … отсюда и такая логика.
(16) Все правильно. Выбор варианта зависит от задачи. 🙂
А такое можно провернуть если таблица ( неподчиненный регистр) учавствует в РИБе?
После truncate получится, что в соответсвующей таблице изменения (_ChnRg которая) будут записи на не существующие записи основной таблицы?
(18) Всё верно. Удалять записи при помощи TRUNCATE нужно в обеих таблицах: основной и регистрации изменений. А в чём вопрос ?
Да, собственно, имеется РИБ (порядка 30 баз) на 8.2. Одна таблица отключается из состава обмена, есть мысль её удалить на sql напрямую. Вопрос — обязательно таблицу изменений еще и чистить, её ведь платформа удалить и так должна. И еще вопрос — если удалить основные записи, а изменения нет, при функционирующих обменах с ней (еще), в таблицах изменения останутся ссылки на несуществующие строки видимо.(?) Как это отразится на обменах?
(20) Добрый день, Сергей!
В Вашем случае все операции типовые — ничего удалять в SQL напрямую не нужно.
Таблица изменений объекта будет удалена автоматически платформой 1С в том случае, когда объект будет исключён из состава всех планов обмена, в которые он входит.
При удалении объектов, они регистрируются в таблице обмена и выгружаются как тип данных УдалениеОбъекта — по сути это контейнер для хранения ссылки или ключа записи удалённого объекта.
Немного не так. Сама таблица чистится (большая её часть, но не вся) чтобы её реструктуризация быстро прошла в последующем обновлении (в неё добавляется колонка и она исключается из обмена). Сама таблица в РИБе, функционирует обмен данными. Скорее всего, будет сделан способ c truncate через вспомогательную таблицу (вот ). Вопрос — обмены не остановятся, битых ссылок не понаделается? Нужно ли еще удалять записи в другой таблице (пока она еще есть в базе)? Вот это непонятно мне…
(21) Дмитрий, подскажите, пожалуйста, что за это за
зверь«контейнер» (УдалениеОбъекта) и для чего он служит (имеется ввиду в обменах типовых конфигураций)?Вот удалил я объект в источнике, далее он регистрируется в таблице изменений как «Объект удалён <УИД>». Что должно произойти после совершения обмена? Удаляться соответствия на эту ссылку в базе приёмнике?
(23) УдалениеОбъекта (встроенная справка 1С):
(22) Стало быть вопрос в реструктуризации. Так бы сразу и сказали.
Ускорение реструктуризации таблиц
Миллионы строк в таблицах 1С? Быстрая реструктуризация — не проблема!
Можно для начала почитать это:
и это:
Нет, вопрос не в реструктуризации.Еще раз процитирую мой вопрос: «…обмены не остановятся, битых ссылок не понаделается? Нужно ли еще удалять записи в другой таблице (пока она еще есть в базе)?» Под «другой» таблицей имеется ввиду таблица изменений.
(26) Задача не понятна. Удаляем записи ради чего ? Свёртка ?
Я понял так: Вы исключаете объект из состава плана обмена, который является единственным, в котором этот объект участвует. В таком случае платформа сама всё за Вас сделает: удалит регистрацию изменений для этого объекта. Затем возник вопрос добавления нового реквизита и реструктуризации. По этому поводу я дал ссылки.
Если всё-таки есть желание поработать на уровне SQL, то отвечаю на вопросы:
Обмены не остановятся ?
Непонятна суть вопроса. Если вопрос про блокировки и прочее, то зависит от уровня изоляции и гранулярности налагаемых блокировок. Для меня термин «обмены остановились» слишком широк. Нужна конкретика.
Битых ссылок не понаделается ?
Если не выполнять контроля ссылочной целостности, то конечно же понаделается.
Нужно ли удалять записи в таблице изменений ?
Ну конечно же нужно. Это тоже касается контроля ссылочной целостности базы.
(24) Но ведь удаление объекта не происходит, если в источнике его удалили и был выполнен обмен =\r
Тем более, если в приёмнике имеются ссылки на него.
(28) Дело происходит так:
1. В источнике удалили объект.
2. В источнике в таблице регистрации изменений зарегистрировалась ссылка на удалённый объект.
3. При выгрузке из источника платформа видит, что ссылка в таблице регистрации изменений «битая» — объект-то удалили. Такую ссылку она выгружает, «завернув» её в УдалениеОбъекта.
4. Приёмник принимает УдалениеОбъекта и знает, что внутри ссылка, которая была удалена в источнике.
Вот для такого сценария в основном и нужен тип «УдалениеОбъекта».
Спасибо за ответы.
Задам еще один: Что будет, если в базе удалить «напрямую» данные из независимого регистра сведений (без регистратора, не имеющего внешних ссылок),и на момент удаления эта таблица имеет «зарегистрированные изменения» в плане обмена. То есть, генерации типа «Удаление объекта» не будет. Как себя поведет 1С при загрузке,выгрузке данных? Что в таком случае придет в «приемник»?
(29) подскажите, пожалуйста, что происходит после приема такого рода сообщения в приёмнике? Не могу в бсп (1.2.х) разобраться.
(30) В таком случае регистрация изменений останется. Источник выгрузит в целевую базу объекты типа УдалениеОбъекта. Приёмник примет их и удалит у себя. Дело в том, что УдалениеОбъекта генерируется при выгрузке. Логика примерно такая: у меня есть изменение, ищу объект по этому изменению в основной таблице, объекта нет — выгружаю УдалениеОбъекта.
(31) Происходит удаление объекта по ссылке или ключу записи.
Ага, спасибо за информацию. Соответсвенно, при массовом удалении будет «куча» таких объектов в обменах.
(33) Без проверки ссылочной целостности???
(35) Конечно же с проверками средствами самой платформы 1С.
(34) Да. Всё верно.
Попробовал это проверить. Удалил, выгрузил из источника, посмотрел xml пакет, а там ничего нового не видно.(1С 8.2.19)
(38) Чтобы понять что и как нужно показать состояние системы до, показать код и состояние системы после выполнения кода. Так я Вам ничего не скажу.