Настройка массового удаления товаров 1С-Битрикс
Старая коллекция снята с производства — 1200 SKU нужно убрать из каталога. Или после импорта прайса обнаружились дубликаты — 300 лишних записей. Удаление через стандартный интерфейс по 20 штук за раз займёт час. При этом каждое удаление тянет за собой каскад операций, который легко положить если делать это без понимания архитектуры.
Что происходит при удалении товара
CIBlockElement::Delete($id) удаляет:
- Запись из
b_iblock_element - Свойства из
b_iblock_element_property - Привязки к разделам из
b_iblock_section_element - Цены из
b_catalog_price - Данные каталога из
b_catalog_product - Штрихкоды из
b_catalog_product_barcode - Файлы через
CFile::Delete()— физически с диска и изb_file - Кэш элемента
При каждом удалении срабатывают события OnBeforeIBlockElementDelete и OnAfterIBlockElementDelete. Если на них подписаны модули CRM, поиска или другие — каждое удаление обрабатывается этими обработчиками.
Массовое удаление без перегрузки сервера
Удаление 1000 элементов в одном запросе создаёт пик нагрузки. Правильный подход — батчевое удаление с паузами:
$toDelete = [1001, 1002, /* ... 1000 id */];
$batchSize = 20;
foreach (array_chunk($toDelete, $batchSize) as $batch) {
foreach ($batch as $id) {
\CIBlockElement::Delete($id);
}
sleep(1); // Пауза между батчами
}
Для очень больших объёмов (10000+) операция запускается как агент с сохранением прогресса:
// Агент записывает оставшиеся ID в b_option и перезапускает себя
$remaining = unserialize(\Bitrix\Main\Config\Option::get('mymodule', 'delete_queue'));
$batch = array_splice($remaining, 0, 20);
foreach ($batch as $id) {
\CIBlockElement::Delete($id);
}
\Bitrix\Main\Config\Option::set('mymodule', 'delete_queue', serialize($remaining));
Деактивация вместо удаления
Физическое удаление необратимо. Для товаров, которые могут вернуться (сезонные, временно снятые), правильнее деактивация — ACTIVE = 'N'. Удаление оправдано только для дубликатов или ошибочно созданных записей.
Перед удалением стоит проверить, нет ли у товара активных заказов. Если товар присутствует в b_sale_basket или b_sale_order_basket — удаление нарушит историческую целостность:
SELECT COUNT(*)
FROM b_sale_order_basket sob
WHERE sob.PRODUCT_ID IN (1001, 1002, 1003)
AND sob.ORDER_ID IN (
SELECT ID FROM b_sale_order WHERE STATUS_ID NOT IN ('F', 'C')
);
Если запрос вернул ненулевое значение — удалять эти товары нельзя, только деактивировать.
Удаление торговых предложений (SKU)
Для товаров с торговыми предложениями (SEO-тип S) нужно сначала удалить все предложения (b_iblock_element из инфоблока предложений), затем основной товар. Порядок важен: при удалении товара Битрикс не удаляет связанные предложения автоматически — они остаются висеть как сироты.
// Получить предложения товара
$offers = \CCatalogSKU::getOffersList(
[$productId],
$catalogIblockId,
[],
['ID'],
[]
);
if (!empty($offers[$productId])) {
foreach ($offers[$productId] as $offer) {
\CIBlockElement::Delete($offer['ID']);
}
}
// Удалить основной товар
\CIBlockElement::Delete($productId);
Очистка файлов
После массового удаления через прямой SQL (если кто-то обходил CIBlockElement::Delete()) файлы в /upload/ остаются на диске. Для их очистки нужно найти ID файлов в b_file записях, которые больше не референсируются из b_iblock_element_property:
SELECT f.ID, f.SUBDIR, f.FILE_NAME
FROM b_file f
LEFT JOIN b_iblock_element_property p ON p.VALUE = CAST(f.ID AS CHAR)
WHERE p.ID IS NULL
AND f.MODULE_ID = 'iblock'
AND f.DATE_CREATE < NOW() - INTERVAL '7 days';
Файлы из результата безопасно удалять через \CFile::Delete($fileId).







