Работа с крупными базами данных на PostgreSQL иногда преподносит сюрпризы, которые программисты называют "магией". Представим ситуацию: вы запускаете простую обработку для синхронизации данных между РегистромСведений и справочником Номенклатура. Код написан верно, транзакции проходят без ошибок, но при проверке запросом вы видите, что данные остались прежними или "перепутались".
В этой статье мы разберем, почему возникает эффект "фантомных записей", как диагностировать дубли в непериодических регистрах и какими способами можно восстановить целостность базы данных.
Рассмотрим сценарий: в конфигурации 1С:Розница на PostgreSQL имеется регистр сведений, где хранится уникальный код для каждой позиции номенклатуры. Задача проста — считать этот код и записать его в реквизит Артикул соответствующей карточки товара. Разработчик использует стандартный алгоритм:
Запрос.ПолучитьОбъект().Артикул и выполнение метода Записать().После завершения обработки выясняется, что часть элементов (например, 55 из 7000) не обновилась. Повторные запуски обработки сообщают об успешной записи, но консоль запросов упрямо показывает старые или некорректные данные. Даже ручное изменение в карточке товара не спасает — в регистре данные "откатываются" или инвертируются.
Проанализируем ситуацию подробнее. Платформа 1С:Предприятие гарантирует уникальность записей в непериодическом регистре сведений по набору его измерений. На уровне СУБД для этого создается уникальный индекс. Однако при использовании PostgreSQL целостность может быть нарушена в следующих случаях:
pg_dump и pg_restore на "горячей" базе (когда пользователи активно записывают данные) без учета транзакционной целостности. Если бэкап снимался без блокировок, индексы и сами таблицы могут рассинхронизироваться.MVCC (многоверсионность) могут допустить ситуацию, когда физически в таблице остаются две строки с одинаковыми ключевыми полями, хотя логически индекс должен это запрещать.В результате при выполнении запроса 1С может получать то одну запись, то другую, в зависимости от того, какой план выполнения выбрала СУБД и какой индекс был задействован. Именно поэтому возникает ощущение, что значения "меняются местами".
Выясним, есть ли в вашем регистре дублирующиеся записи. Для этого воспользуемся механизмом группировки в языке запросов 1С. Если мы сгруппируем данные по всем измерениям и посчитаем количество строк, в идеальной ситуации Количество всегда будет равно 1.
Рассмотрим пример запроса для поиска проблемных мест:
ВЫБРАТЬ
РегистрДанных.Номенклатура КАК Номенклатура,
КОЛИЧЕСТВО(*) КАК КоличествоСтрок
ИЗ
РегистрСведений.ВашРегистр КАК РегистрДанных
СГРУППИРОВАТЬ ПО
РегистрДанных.Номенклатура
ИМЕЮЩИЕ
КОЛИЧЕСТВО(*) > 1
Если этот запрос вернул строки — поздравляем, вы нашли причину "магии". Платформа видит несколько физических записей для одного измерения Номенклатура, чего в норме быть не должно.
Это самый надежный "штатный" способ исправления логической целостности базы. Проанализируем, как это работает: при выгрузке в файл .dt платформа 1С последовательно считывает данные из таблиц. При последующей загрузке в новую (пустую) базу данных платформа заново создает структуру таблиц и индексов в PostgreSQL.
Важный нюанс: Если в данных действительно есть дубли, процесс загрузки .dt может прерваться с ошибкой "Запись с такими ключевыми полями уже существует". В этом случае платформа сама укажет на проблемный объект, и вам придется удалить лишние записи на уровне SQL, прежде чем повторить попытку.
Если база слишком велика для выгрузки в .dt, проблему можно решить непосредственно в pgAdmin или через консоль psql. Разберем шаги:
ПолучитьСтруктуруХраненияБазыДанных()._Fld).ctid (физический адрес строки в PostgreSQL), чтобы оставить только одну версию данных.Пример концептуального SQL-запроса для поиска дублей (имена полей будут зависеть от вашей конфигурации):
SELECT _fld123_idrref, COUNT(*)
FROM _inforg456
GROUP BY _fld123_idrref
HAVING COUNT(*) > 1;
После удаления дублей крайне рекомендуется выполнить команду REINDEX TABLE имя_таблицы, чтобы перестроить индексы и убедиться, что ограничение уникальности снова работает корректно.
После того как дубли в регистре будут устранены, нам нужно гарантировать, что справочник Номенклатура содержит актуальные данные. Посмотрим на пример кода, который поможет безопасно обновить артикулы, исключая возможные ошибки блокировок:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КодыНоменклатуры.Номенклатура КАК Ссылка,
| КодыНоменклатуры.Код КАК НовыйКод
|ИЗ
| РегистрСведений.КодыНоменклатуры КАК КодыНоменклатуры";
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
ОбъектНом = Выборка.Ссылка.ПолучитьОбъект();
// Проверяем, нужно ли реально менять значение, чтобы лишний раз не записывать
Если ОбъектНом.Артикул <> Выборка.НовыйКод Тогда
ОбъектНом.Артикул = Выборка.НовыйКод;
Попытка
ОбъектНом.Записать();
Сообщить("Обновлен артикул для: " + Выборка.Ссылка);
Исключение
Сообщить("Ошибка при записи: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЦикла;
Чтобы "магия" не возвращалась, проанализируем регламенты обслуживания вашей базы данных PostgreSQL. Рассмотрим основные рекомендации:
.dt по расписанию. Это гарантирует отсутствие дублей.pg_dump, убедитесь, что вы используете режим --format=custom и при восстановлении через pg_restore внимательно следите за логами на предмет ошибок создания индексов.Подведем итог: если вы столкнулись с необъяснимым поведением данных в 1С на серверной базе, первым делом проверьте наличие физических дублей в регистрах. В 99% случаев за "магией" скрывается нарушение уникальности записей, вызванное сбоями при копировании или обслуживании СУБД.