Как исправить расхождение datetimeiddoc в таблицах _1SJOURN и _1SCRDOC в 1С 7.7 и что означают странные MDID?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

Работа с базой данных 1С:Предприятие 7.7 в SQL-варианте таит в себе немало загадок, особенно когда дело касается внутренней структуры таблиц и поддержания ссылочной целостности. Одной из наиболее частых проблем, с которой сталкиваются администраторы и разработчики крупных баз данных, является рассинхронизация времени документов между основным журналом _1SJOURN и служебной таблицей связей _1SCRDOC. В дополнение к этому при анализе таблиц СУБД часто обнаруживаются странные записи с незаполненными или специфическими идентификаторами метаданных (MDID), у которых поле значения родителя (PARENTVAL) заполнено символом 'U'.

В этой статье мы подробно разберем, как устроена таблица перекрестных ссылок _1SCRDOC, выясним назначение всех типов записей в ней (включая загадочные строки с маркером 'U'), проанализируем причины расхождения дат документов и разработаем пошаговый алгоритм безопасного автоматического исправления этих данных напрямую через СУБД MS SQL Server.

Анатомия таблицы _1SCRDOC: для чего она нужна и что в ней хранится?

Таблица _1SCRDOC является одной из важнейших системных таблиц в архитектуре 1С 7.7. Ее основное назначение — обеспечивать быструю выборку документов по связям подчиненности, графам отбора и дополнительным журналам. Без этой таблицы СУБД приходилось бы выполнять тяжелые сканирования таблиц документов при открытии любого отбираемого журнала или дерева подчиненности.

Проанализируем структуру основных колонок этой таблицы:

Рассмотрим подробнее три принципиально разные категории записей, которые могут находиться в этой таблице в зависимости от значения поля MDID:

  1. Структура подчиненности документов (связи Документ-Документ):

    Для таких записей поле MDID всегда принимает значение 0. В поле PARENTVAL записывается идентификатор родительского документа с системным префиксом (например, O1 + ID вида документа + ID самого документа). Благодаря этим записям платформа мгновенно строит дерево подчиненности для любого документа.

  2. Графы отбора (отборы в общих журналах):

    Если в конфигурации созданы графы отбора (например, «Контрагент», «Договор», «Склад»), то для каждого значения графы отбора, указанного в документе, в таблицу _1SCRDOC пишется строка. При этом MDID равен внутреннему идентификатору этой графы отбора из метаданных (в вашей конфигурации это могут быть числа вроде 1019, 4265 и т.д.). В поле PARENTVAL записывается конкретная ссылка на элемент справочника или документ, по которому выполняется фильтрация.

  3. Дополнительные (общие) журналы документов:

    Это и есть те самые «странные» записи, которые вызывают массу вопросов у разработчиков. Когда в конфигураторе создается дополнительный журнал, в свойствах которого указывается состав входящих в него документов, платформа должна где-то фиксировать принадлежность конкретных документов к этому журналу. Для этого используется таблица _1SCRDOC. В этом случае:

    • Поле MDID содержит идентификатор этого дополнительного журнала из метаданных.
    • Поле PARENTVAL всегда принимает фиксированное текстовое значение 'U ' (буква U и последующие пробелы до конца длины поля). Это системная константа-маркер платформы 1С 7.7, сигнализирующая о том, что запись описывает факт вхождения документа в дополнительный журнал.

Таким образом, наличие записей с PARENTVAL = 'U' — это абсолютно штатное поведение платформы, отражающее структуру ваших дополнительных журналов документов. Удалять эти записи «вслепую» нельзя, так как из дополнительных журналов пропадут соответствующие документы!

Выясняем причины расхождения дат между _1SJOURN и _1SCRDOC

Теперь разберем природу возникновения рассинхронизации. Почему же периодически расходятся значения DATE_TIME_IDDOC в общем журнале _1SJOURN и CHILD_DATE_TIME_IDDOC в таблице связей _1SCRDOC, а также в таблицах операций _1SOPER и проводок _1SENTRY?

Основной источник проблемы кроется в механизме позиционирования документов внутри одной секунды, реализованном в 1С 7.7. Когда пользователь записывает или проводит документ, платформа пытается автоматически распределить документы, созданные в одну и ту же секунду, сдвигая их время вперед на микросекундном уровне (в терминах 1С 7.7 это выражается в изменении текстового представления времени в тридцатишестиричной системе счисления, например, превращение 23:59:59 из 20231031EAEAY8 в новое смещенное значение 20231031EAGG40).

В процессе такого автоматического сдвига времени платформа инициирует транзакцию обновления данных во всех связанных таблицах. Однако в условиях высокой конкурентной работы, блокировок таблиц на уровне СУБД MS SQL или аварийного завершения сессий пользователей могут возникать ситуации, когда:

В результате мы получаем «битую» транзакцию на уровне логики 1С: в общем журнале у документа новое время, а в проводках и подчиненных связях осталось старое. Самое неприятное в этой ситуации заключается в том, что при попытке выполнить ТИИ (Тестирование и исправление), реструктуризацию таблиц или просто при создании новой графы отбора Конфигуратор считывает данные из _1SJOURN и пытается пересоздать записи в _1SCRDOC. Натыкаясь на несовпадение дат и уже существующие ключи, СУБД выбрасывает критическую ошибку:

SQL State: 23000, Native: 1505 (CREATE UNIQUE INDEX terminated because a duplicate key was found). Дальнейшая работа по обновлению метаданных или тестированию базы становится невозможной.

Пошаговая инструкция по автоматическому лечению расхождений

Для того чтобы исправить сложившуюся ситуацию без монопольного доступа и многочасового ожидания полного пересчета служебных данных в Конфигураторе, мы применим точечные SQL-скрипты. Рассмотрим этот процесс по шагам.

Шаг 1. Поиск документов с расхождениями во времени

Прежде чем что-то менять, нам нужно локализовать проблему и оценить масштаб расхождений. Выполним проверочный запрос, который находит расхождения времени между общим журналом документов _1SJOURN, таблицей операций _1SOPER и таблицей бухгалтерских проводок _1SENTRY. За эталон мы всегда принимаем время из общего журнала _1SJOURN.


SELECT 
    o.docid AS [IDDOC],
    j.docno AS [НомерДокумента],
    j.date_time_iddoc AS [Время_в_Журнале],
    o.date_time_docid AS [Время_в_Операциях],
    s.date_time_docid AS [Время_в_Проводках]
FROM _1SOPER o (NOLOCK)
LEFT JOIN _1sentry s (NOLOCK) ON s.docid = o.docid
INNER JOIN _1SJOURN j (NOLOCK) ON j.iddoc = o.docid
WHERE (s.date_time_docid IS NOT NULL AND s.date_time_docid <> j.date_time_iddoc)
   OR o.date_time_docid <> j.date_time_iddoc;

Если этот запрос вернул строки, значит, в вашей базе данных действительно есть документы с нарушенной целостностью времени проведения. Их необходимо вылечить.

Шаг 2. Синхронизация времени в таблицах операций и проводок

Выполним принудительное обновление времени в таблицах _1SOPER и _1SENTRY. Мы подтянем актуальное время проведения из главного журнала _1SJOURN. Обратите внимание, что выполнять эти операции рекомендуется в периоды минимальной активности пользователей, предварительно сделав резервную копию базы данных.


-- Корректируем время в таблице бухгалтерских проводок
UPDATE _1sentry 
SET _1sentry.DATE_TIME_DOCID = j.DATE_TIME_IDDOC
FROM _1sentry s
INNER JOIN _1SJOURN j ON s.DOCID = j.IDDOC
WHERE s.DATE_TIME_DOCID <> j.DATE_TIME_IDDOC;

-- Корректируем время в таблице операций (документов)
UPDATE _1SOPER 
SET _1SOPER.DATE_TIME_DOCID = j.DATE_TIME_IDDOC
FROM _1SOPER o
INNER JOIN _1SJOURN j ON o.DOCID = j.IDDOC
WHERE o.DATE_TIME_DOCID <> j.DATE_TIME_IDDOC;

Шаг 3. Синхронизация времени в таблице связей _1SCRDOC

Теперь переходим к нашей целевой таблице _1SCRDOC. Нам необходимо обновить поле времени дочернего документа CHILD_DATE_TIME_IDDOC, чтобы оно строго соответствовало времени проведения этого документа из таблицы общего журнала _1SJOURN.


UPDATE _1scrdoc 
SET _1scrdoc.CHILD_DATE_TIME_IDDOC = j.DATE_TIME_IDDOC
FROM _1scrdoc c
INNER JOIN _1SJOURN j ON c.CHILDID = j.IDDOC
WHERE c.CHILD_DATE_TIME_IDDOC <> j.DATE_TIME_IDDOC;

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

Шаг 4. Борьба с дубликатами в таблице _1SCRDOC

После проведения массовой синхронизации времени в таблице _1SCRDOC может возникнуть побочный эффект. Из-за того, что ранее в таблице существовали записи с одинаковыми ключевыми полями (CHILDID, MDID, PARENTVAL), но отличающиеся только устаревшим временем в поле CHILD_DATE_TIME_IDDOC, после обновления эти записи станут абсолютно идентичными дубликатами.

Наличие полных дубликатов строк нарушает уникальность индексов и приведет к ошибке при первой же реструктуризации базы данных в Конфигураторе. Нам необходимо избавиться от дублей, оставив ровно по одной уникальной строке на каждую связку метаданных и документов. Для этого выполним следующий запрос удаления:


DELETE FROM _1SCRDOC
WHERE ROW_ID NOT IN (
    SELECT MIN(ROW_ID)
    FROM _1SCRDOC
    GROUP BY CHILDID, MDID, PARENTVAL
);

В данном случае колонка ROW_ID (автоинкрементное поле, присутствующее в таблицах 1С на MS SQL) выступает гарантом того, что мы сохраним ровно по одной уникальной строке для каждой связи, физически удалив все лишние дубликаты, которые образовались в результате сбоев транзакций.

Автоматизация процесса контроля целостности

Для того чтобы забыть о ручном исправлении подобных ошибок и исключить возникновение критических ситуаций во время планового обновления конфигурации, рекомендуется настроить автоматическое «самолечение» базы данных. Для этого мы можем объединить все вышеперечисленные операции в единый транзакционный скрипт и добавить его в регламентное задание (Job) на SQL Server.

Создайте новое регламентное задание в MS SQL Server Agent со следующим шагом выполнения (класс расписания — ежедневно, например, в 03:00 ночи):


BEGIN TRANSACTION;

BEGIN TRY
    -- 1. Исправление времени в проводках
    UPDATE s
    SET s.DATE_TIME_DOCID = j.DATE_TIME_IDDOC
    FROM _1sentry s
    INNER JOIN _1SJOURN j ON s.DOCID = j.IDDOC
    WHERE s.DATE_TIME_DOCID <> j.DATE_TIME_IDDOC;

    -- 2. Исправление времени в операциях
    UPDATE o
    SET o.DATE_TIME_DOCID = j.DATE_TIME_IDDOC
    FROM _1SOPER o
    INNER JOIN _1SJOURN j ON o.DOCID = j.IDDOC
    WHERE o.DATE_TIME_DOCID <> j.DATE_TIME_IDDOC;

    -- 3. Исправление времени в таблице подчиненности и отборов
    UPDATE c
    SET c.CHILD_DATE_TIME_IDDOC = j.DATE_TIME_IDDOC
    FROM _1scrdoc c
    INNER JOIN _1SJOURN j ON c.CHILDID = j.IDDOC
    WHERE c.CHILD_DATE_TIME_IDDOC <> j.DATE_TIME_IDDOC;

    -- 4. Очистка дубликатов в таблице связей
    DELETE FROM _1SCRDOC
    WHERE ROW_ID NOT IN (
        SELECT MIN(ROW_ID)
        FROM _1SCRDOC
        GROUP BY CHILDID, MDID, PARENTVAL
    );

    COMMIT TRANSACTION;
    PRINT 'Синхронизация таблиц документов выполнена успешно.';
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
    DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();
    RAISERROR(@ErrorMessage, 16, 1);
END CATCH;

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

Внедрение данного регламентного скрипта позволяет полностью решить проблему расхождения дат в 1С 7.7, защитит СУБД от накопления неконсистентных данных и исключит появление ошибок уникальности индексов при работе с конфигурацией.

← На главную