При оптимизации учетных систем на платформе 1С:Предприятие 7.7 с использованием прямого доступа к базе данных MS SQL разработчики часто сталкиваются с задачей выборки подчиненных документов. Попытка отфильтровать документы с помощью строковых функций вроде SUBSTRING() приводит к катастрофическому падению производительности. Разберем по шагам, почему это происходит, как устроен индекс таблицы связей и как составить быстрый SQL-запрос для одного документа или целого списка.
В базе данных 1С 7.7 информация о связях подчиненности хранится в системной таблице _1SCRDOC. Для ускорения поиска по этой таблице создан составной индекс PARENT, включающий следующие поля:
MDID — идентификатор реквизита отбора (графы отбора);PARENTVAL — 23-символьное типизированное строковое представление документа-родителя;CHILDDATE — дата подчиненного документа;CHILDTIME — время подчиненного документа;CHILDID — 9-значный 36-ричный идентификатор (IDDOC) подчиненного документа.Когда в секции WHERE мы применяем функцию к индексированному полю (например, SUBSTRING(Подчиненные.PARENTVAL, 7, 9)), СУБД теряет возможность использовать индекс (нарушается условие SARGability). Вместо мгновенного поиска по индексу SQL Server выполняет полное сканирование таблицы (Table Scan / Index Scan), перебирая миллионы записей.
Вторая распространенная ошибка — отсутствие или некорректное указание первого поля индекса MDID. Если первое поле составного индекса не участвует в условии фильтрации, индекс по последующим полям также не может работать эффективно.
Поле MDID в таблице _1SCRDOC имеет тип CHAR(4). Для системных связей подчиненности (документ-основание) значение этого поля всегда равно нулю, дополненному тремя пробелами слева: ' 0'.
Важно: Нельзя писать MDID = 0 без кавычек, так как при неявном приведении типов строковое поле преобразуется в число, что снова приведет к игнорированию индекса. Необходимо всегда передавать точное строковое значение с пробелами:
WHERE Подчиненные.MDID = ' 0'
AND Подчиненные.PARENTVAL = :ВыбДокумент
Чтобы выполнить точный поиск по полю PARENTVAL, проанализируем его внутреннюю структуру. Полная строка длиной в 23 символа формируется по жестким правилам платформы 1С 7.7:
O1 (латинская буква 'O' и цифра 1).IDDOCDEF из таблицы _1SJOURN), выровненный по правому краю с пробелами слева.IDDOC).Если мы формируем значение прямо в SQL-запросе при выборке из журнала _1SJOURN, склейка полного представления выглядит следующим образом:
'O1' + RIGHT(SPACE(4) + CAST(Журнал.IDDOCDEF AS VARCHAR(4)), 4) + Журнал.IDDOC + SPACE(8)
Рассмотрим, как быстро выбрать подчиненные документы для целого списка документов. При использовании внешних компонент для прямых запросов (например, 1С++) список документов выгружается во временную таблицу методом УложитьСписокОбъектов(), который автоматически генерирует поле Val с правильной 23-значной строкой.
Посмотрим на пример эффективного запроса, использующего соединение по индексу вместо медленного оператора IN:
Запрос = СоздатьОбъект("ODBCRecordset");
Запрос.УложитьСписокОбъектов(СписокРодительскихДокументов, "#ВыбДокументы", "Документ");
ТекстЗапроса = "
|SELECT
| Журнал.IDDOC AS [Документ $Документ],
| Журнал.IDDOCDEF AS [Документ_вид]
|FROM
| _1SCRDOC AS Подчиненные WITH (NOLOCK)
|INNER JOIN
| #ВыбДокументы AS СписокРодителей ON Подчиненные.PARENTVAL = СписокРодителей.Val
|INNER JOIN
| _1SJOURN AS Журнал WITH (NOLOCK) ON Журнал.IDDOC = Подчиненные.CHILDID
|WHERE
| Подчиненные.MDID = ' 0'
| AND Журнал.ISMARK = 0
|";
При работе с подчиненными документами через прямые запросы обратим внимание на следующие нюансы:
_1SCRDOC фиксирует только те связи, которые создаются платформенным механизмом ввода на основании. Если в конфигурации связь между документами реализована через произвольный реквизит шапки, такие документы в _1SCRDOC не отображаются.CHILDID содержит только идентификатор документа без сведений о его проведении или пометке на удаление. Для исключения удаленных объектов обязательно соединяем выборку с _1SJOURN и проверяем условие ISMARK = 0.