Написание запросов в системе 1С:Предприятие часто вызывает вопросы у разработчиков, привыкших к классическому SQL. Несмотря на то, что язык запросов 1С является его диалектом, он имеет ряд архитектурных особенностей, связанных с объектной моделью данных. В этой статье мы разберем, как эффективно извлекать данные из табличных частей, правильно соединять таблицы и избегать типичных ошибок при работе с условиями и временными таблицами.
Проанализируем базовый принцип хранения данных. В 1С справочник — это не одна плоская таблица. Каждый справочник представлен основной таблицей (шапкой), а каждая его табличная часть — это отдельная физическая таблица в базе данных. Связь между ними осуществляется по уникальному идентификатору, который в 1С называется Ссылка. Рассмотрим пример: если у нас есть справочник Контрагенты с табличной частью КонтактныеЛица, то в SQL-представлении это будут две разные таблицы, где табличная часть содержит поле Ссылка, указывающее на владельца записи.
Разберем ситуацию, когда нам необходимо получить количество строк в табличной части для каждого элемента справочника. Посмотрим на пример запроса, который позволяет это сделать:
ВЫБРАТЬ
ТЧ.Ссылка КАК ДокументСсылка,
КОЛИЧЕСТВО(ТЧ.НомерСтроки) КАК КоличествоСтрок
ИЗ
Справочник.МойСправочник.ИмяТабличнойЧАсти КАК ТЧ
СГРУППИРОВАТЬ ПО
ТЧ.Ссылка
Важный нюанс: результат выполнения запроса в 1С — это объект РезультатЗапроса. Чтобы получить из него конкретное число, у нас есть два пути:
Запрос.Выполнить().Выбрать(). В этом случае нужно вызвать метод Следующий(), чтобы обратиться к данным.Запрос.Выполнить().Выгрузить(). Это удобно, если данных немного и их нужно обработать программно.Выясним, как одновременно получить данные из реквизитов самого справочника (например, Наименование или ИНН) и данные из его табличной части. Существует два подхода. Первый — использование «разыменования» через точку, что является особенностью 1С. Мы можем написать ТЧ.Ссылка.Наименование, и система сама создаст неявное левое соединение. Однако, проанализируем ситуацию с точки зрения производительности: при большом количестве обращений через точку запрос может начать работать медленно.
Второй, более профессиональный подход — явное использование конструкции ЛЕВОЕ СОЕДИНЕНИЕ. Рассмотрим пример:
ВЫБРАТЬ
Спр.Наименование,
ТЧ.ФизЛицо
ИЗ
Справочник.ЛицевыеСчета КАК Спр
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ЛицевыеСчета.СписокЖильцов КАК ТЧ
ПО Спр.Ссылка = ТЧ.Ссылка
Разберем типичную ошибку новичков при использовании ЛЕВОГО СОЕДИНЕНИЯ. Когда мы присоединяем вторую таблицу и накладываем условие на её поля в секции ГДЕ, наше левое соединение фактически превращается во внутреннее. Это происходит потому, что для тех записей, которых нет во второй таблице, значения полей будут NULL. Любое сравнение NULL с конкретным значением возвращает ложь, и строка отсекается из результата.
Проанализируем решение этой проблемы. Чтобы сохранить все записи из первой таблицы, условие по второй таблице нужно перенести непосредственно в конструкцию ПО (условие связи). Сравним два варианта:
Неправильно:
ГДЕ
ЛицевыеСчета.ПометкаУдаления = ЛОЖЬ
И ЛицевыеСчетаСписокЖильцов.ДатаОкончания = &ПустаяДата
Правильно:
ИЗ
Справочник.ЛицевыеСчета КАК ЛицевыеСчета
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ЛицевыеСчета.СписокЖильцов КАК СписокЖильцов
ПО ЛицевыеСчета.Ссылка = СписокЖильцов.Ссылка
И СписокЖильцов.ДатаОкончания = &ПустаяДата
ГДЕ
ЛицевыеСчета.ПометкаУдаления = ЛОЖЬ
При построении сложных отчетов часто используется пакетное выполнение запросов с помещением промежуточных итогов во временные таблицы. Рассмотрим ситуацию, когда мы соединяем две временные таблицы и хотим сложить числовые показатели. Если во второй таблице данных для какой-то ссылки не нашлось, система вернет NULL. Помните: любая математическая операция с NULL (например, 100 + NULL) всегда дает результат NULL.
Для корректного суммирования необходимо использовать функцию ЕСТЬNULL. Она проверяет значение первого параметра, и если там NULL, подставляет значение второго параметра. Посмотрим на пример:
ВЫБРАТЬ
ВТ1.Ссылка,
ВТ1.Сумма1 + ЕСТЬNULL(ВТ2.Сумма2, 0) КАК ИтоговаяСумма
ИЗ
ВТ_ПервыеДанные КАК ВТ1
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВторыеДанные КАК ВТ2
ПО ВТ1.Ссылка = ВТ2.Ссылка
Рассмотрим еще несколько важных моментов, которые помогут писать запросы в 1С более качественно:
ИНДЕКСИРОВАТЬ ПО для тех полей, по которым в дальнейшем будет производиться соединение.ГДЕ. Это значительно ускоряет работу SQL-сервера.ВЫРАЗИТЬ для полей составного типа. Это помогает платформе точнее определить структуру запроса и не делать лишних соединений со всеми таблицами, входящими в состав типа.РАЗРЕШЕННЫЕ после оператора ВЫБРАТЬ, чтобы избежать ошибок при нехватке прав у пользователя на конкретные строки.Таким образом, переход от SQL к запросам 1С требует понимания метаданных системы. Использование явных соединений, грамотная работа с NULL и применение временных таблиц — это залог создания надежного и быстрого кода.