При работе с высоконагруженными базами на платформе 1С 7.7 стандартных средств работы с регистрами часто становится недостаточно. Программисты прибегают к прямым запросам (Direct Queries) через внешние компоненты, такие как 1C++. Однако здесь возникают специфические сложности: необходимость вручную обрабатывать иерархию справочников и корректно приводить типы данных SQL к формату 1С. Разберем ситуацию, когда нам нужно найти последний документ прихода по списку номенклатуры, исключив при этом поставщиков из «запретной» папки, и при этом избежать ошибок при отсутствии движений.
В прямых запросах SQL не «знает» о структуре папок 1С. Чтобы отсеять контрагентов, находящихся в определенной группе, мы не можем просто использовать стандартное условие В Группе. Проанализируем наиболее эффективный подход к решению этой задачи.
Для начала нам необходимо собрать все идентификаторы (ID) контрагентов, которые входят в «запретную» папку. Рассмотрим два способа реализации этой фильтрации:
#TableKontr) и наполняем ее списком ID всех поставщиков из иерархии. Если используется 1C++, удобнее всего применить метод УложитьСписокОбъектов().INNER JOIN к справочнику партий, так как в регистре ПартииНаличие поставщик обычно хранится как реквизит партии.Посмотрим на пример реализации фильтрации внутри подзапроса:
INNER JOIN
$Справочник.Партии AS СпрП ON СпрП.ID = $Рег.Партия
INNER JOIN
$Справочник.Контрагенты AS СпрК ON СпрК.ID = $СпрП.Поставщик
WHERE
$СпрП.Поставщик NOT IN (SELECT val FROM #TableKontr)
В данном случае мы сначала соединяемся с таблицей партий, чтобы «дотянуться» до поставщика, а затем отсекаем тех, чьи ID присутствуют в нашей временной таблице «запрещенных» контрагентов.
Часто при выполнении запроса возникает сообщение: «Ошибка преобразования даты или времени из символьной строки». Это происходит из-за того, что в 1С 7.7 дата и время в SQL хранятся в специфическом строковом формате Date_Time_IDDOC (23 символа), где первые 8 символов — это дата в формате YYYYMMDD. Если для какой-то номенклатуры прихода не было, функция MAX() может вернуть NULL, или попытка обрезать пустую строку приведет к некорректному результату при CAST.
Проанализируем ситуацию: для 1С «пустая» дата в SQL соответствует значению 1753-01-01. Чтобы запрос работал стабильно, нам нужно предусмотреть обработку NULL с помощью функции ISNULL.
Разберем правильный подход к получению даты:
LEFT(Рег.Date_Time_IDDOC, 8), чтобы выделить строковую дату.ISNULL(..., '17530101'), чтобы заменить отсутствие данных на пустую дату 1С.CONVERT со стилем 112 (ISO формат YYYYMMDD), который SQL Server понимает однозначно вне зависимости от локальных настроек сервера.Рассмотрим пример кода для обработки даты в итоговом выборе:
SELECT
temp.val,
Спр.Code AS Код,
subsel.Номенклатура AS [Номенклатура $Справочник.Номенклатура],
ISNULL(CONVERT(datetime, LEFT(subsel.ДатаПослПрихода, 8), 112), '17530101') AS [ДатаПрих]
FROM
#TempTable AS temp
LEFT JOIN
(
SELECT
$Рег.Номенклатура AS [Номенклатура],
MAX(Рег.Date_Time_IDDOC) AS ДатаПослПрихода
FROM
$Регистр.ПартииНаличие as Рег
WHERE
$Рег.Номенклатура IN (SELECT val FROM #TempTable)
GROUP BY
$Рег.Номенклатура
) AS subsel ON temp.val = subsel.Номенклатура
Поле Date_Time_IDDOC — это уникальный составной индекс. Выясним причину, по которой лучше использовать MAX(Date_Time_IDDOC) целиком, а не только дату:
Во-первых, если в одну секунду было проведено несколько документов поступления, использование только даты в MAX() не позволит определить, какой документ был действительно последним. Сравнение всей строки Date_Time_IDDOC гарантирует выбор самого последнего документа в хронологической последовательности системы.
Во-вторых, при последующем соединении (JOIN) основного запроса с таблицей регистра или журнала документов, связь по полному 23-символьному значению Date_Time_IDDOC работает значительно быстрее, так как это поле проиндексировано в SQL.
При написании прямых запросов к регистрам (особенно к ПартииНаличие, который может быть очень объемным), крайне важно придерживаться правил оптимизации:
(NOLOCK) после названия таблиц. Это предотвратит блокировки таблиц при чтении данных, что критично для работы пользователей в многопользовательском режиме._1Sjourn (журналу документов) с условием iddocdef = $ВидДокумента.Имя позволяет SQL использовать существующие индексы._1Sjourn по полю IDDoc.Посмотрим на пример того, как может выглядеть финальный, оптимизированный фрагмент кода:
|SELECT
| $Рег.Номенклатура AS [Номенклатура],
| MAX(Рег.Date_Time_IDDOC) AS Позиция
|FROM
| $Регистр.ПартииНаличие AS Рег (NOLOCK)
|INNER JOIN
| _1Sjourn AS Журнал (NOLOCK) ON Журнал.IDDoc = Рег.IDDoc
|INNER JOIN
| $Справочник.Партии AS СпрП (NOLOCK) ON СпрП.ID = $Рег.Партия
|WHERE
| Журнал.iddocdef = $ВидДокумента.ПоступлениеТМЦ_Склад
| AND $Рег.Номенклатура IN (SELECT val FROM #TempTable)
| AND $СпрП.Поставщик NOT IN (SELECT val FROM #TableKontr)
|GROUP BY
| $Рег.Номенклатура
Подведем итог: для решения «задачи с подвохом» мы разделили логику на получение списка разрешенных контрагентов и корректный поиск последнего движения в SQL. Использование функций ISNULL и CONVERT позволило нам избежать ошибок преобразования типов, а работа с полным значением Date_Time_IDDOC обеспечила точность и высокую скорость выполнения запроса.