← вся теориятеория к собесу · Язык запросов 1С

Функции языка запросов 1С: ЕСТЬNULL, ВЫБОР, РАЗНОСТЬДАТ

разбор писал Даниил, автор Сеньорчика

Зачем это спрашивают

Функции языка запросов закрывают NULL, режут даты на периоды, считают разность дат и строят условные значения. На собесе спрашивают про ЕСТЬNULL и ЕСТЬ NULL, про РАЗНОСТЬДАТ на границе суток и про то, почему ГОД(Дата) в условии тормозит. Результаты сверены на PostgreSQL.

// Так спрашивают: «Чем ЕСТЬNULL отличается от ЕСТЬ NULL?» «Сколько вернёт РАЗНОСТЬДАТ между 23:00 и 01:00 следующего дня в днях?» «Что вернёт СУММА по пустой выборке?» «Почему ГОД(Дата) = 2026 в условии плохо?»

ЕСТЬNULL и ЕСТЬ NULL

ЕСТЬNULL с двумя аргументами заменяет NULL значением: ЕСТЬNULL(Остатки.Количество, 0). Проверка на NULL в условии пишется раздельно: Поле ЕСТЬ NULL. Равенство Поле = NULL не срабатывает никогда, потому что любое сравнение с NULL не истинно.

Агрегат по пустому набору без группировки возвращает одну строку с NULL, а не ноль. Продажи за день, когда продаж не было:

Если такую сумму сложить в коде с числом, расчёт упадёт на ошибке преобразования NULL. ЕСТЬNULL в самом запросе решает это раз и навсегда.

ВЫБРАТЬ
    СУММА(Р.СуммаДокумента) КАК Сумма,
    ЕСТЬNULL(СУММА(Р.СуммаДокумента), 0) КАК СуммаИлиНоль
ИЗ
    Документ.Реализация КАК Р
ГДЕ
    Р.Дата МЕЖДУ &НачалоДня И &КонецДня

Даты

НАЧАЛОПЕРИОДА(Дата, МЕСЯЦ) приводит дату к началу месяца, по ней группируют продажи по месяцам. ДОБАВИТЬКДАТЕ(Дата, ДЕНЬ, 7) сдвигает дату. ДАТАВРЕМЯ(2026, 10, 1) принимает только числа-константы, из полей дату им не собрать.

РАЗНОСТЬДАТ(&Начало, &Конец, ДЕНЬ) считает, сколько границ суток пересекли, а не сколько полных суток прошло. Поэтому 23:00 и 01:00 следующего дня дают 1, хотя прошло два часа:

Для просрочки «больше трёх суток» по времени такая разность врёт, там считают в секундах или часах. Для «сколько календарных дней между датами документов» она как раз подходит.

Функция над полем в условии ломает индекс: ГОД(Р.Дата) = 2026 заставит СУБД (система управления базами данных) посчитать год у каждой строки. Тот же отбор диапазоном Р.Дата МЕЖДУ &Начало И &Конец идёт по индексу по дате.

ВЫБРАТЬ
    РАЗНОСТЬДАТ(&Вечер, &Ночь, ДЕНЬ) КАК Дней,
    РАЗНОСТЬДАТ(&Вечер, &Ночь, ЧАС) КАК Часов
// &Вечер = 14.10.2026 23:00, &Ночь = 15.10.2026 01:00

ВЫБОР и ПРЕДСТАВЛЕНИЕ

ВЫБОР строит значение по условию, это аналог CASE из SQL. Ветки разных типов дают составной тип колонки, поэтому ветки приводят к одному типу. Разложим суммы продаж по размеру:

ВЫБОР проверяет ветки сверху вниз и берёт первую подходящую, поэтому 1 200 попала в «крупную», а не в «среднюю». ПРЕДСТАВЛЕНИЕ(Р.Контрагент) получает текст ссылки прямо в запросе, без отдельного обращения к базе на каждую строку при выводе.

ВЫБРАТЬ
    Р.Номер,
    ВЫБОР
        КОГДА Р.СуммаДокумента >= 1000 ТОГДА "крупная"
        КОГДА Р.СуммаДокумента >= 500 ТОГДА "средняя"
        ИНАЧЕ "мелкая"
    КОНЕЦ КАК Размер
ИЗ
    Документ.Реализация КАК Р
УПОРЯДОЧИТЬ ПО
    Р.Номер

Как отвечать: «Почему отчёт падает на пустом периоде, хотя на заполненном работает?»

Скорее всего в запросе агрегат без группировки, например СУММА по продажам. На пустом наборе он возвращает не ноль, а одну строку с NULL, и код дальше пытается сложить NULL с числом. Лечится ЕСТЬNULL(СУММА(...), 0) прямо в запросе. Заодно проверю левые соединения: у строк без пары там тоже NULL.

Ответ находит причину по симптому «падает только на пустом» и лечит её в запросе, а не проверками в коде. Упоминание левых соединений показывает системный взгляд на NULL.

На чём валят

  • −Ждут ноль от СУММА по пустому набору. Без группировки вернётся одна строка с NULL. Оборачивают ЕСТЬNULL.
  • −Проверяют Поле = NULL. Сравнение с NULL не истинно. Пишут Поле ЕСТЬ NULL.
  • −Пишут ГОД(Дата) = 2026 в условии. Функция над полем ломает индекс. Отбор диапазоном дат.
  • −Считают просрочку в сутках через РАЗНОСТЬДАТ(…, ДЕНЬ). Функция считает границы суток: 23:00 и 01:00 дают 1. Просрочку по времени считают в секундах или часах.

Проверь себя

Пять вопросов из банка по этой подтеме. Всего их 12, остальные разбираются в тренажёре.

  1. #oc_q_functions1 / 5
    Как получить в запросе начало месяца для даты документа?
    A)НАЧАЛОПЕРИОДА(Д.Дата, МЕСЯЦ)
    B)ДАТАВРЕМЯ(ГОД(Д.Дата), МЕСЯЦ(Д.Дата), 1)
    C)ОКРУГЛИТЬ(Д.Дата, МЕСЯЦ)
    D)НачалоМесяца(Д.Дата)
    показать ответ и разбор
    +A)`НАЧАЛОПЕРИОДА(Д.Дата, МЕСЯЦ)`

    // разбор: НАЧАЛОПЕРИОДА(Дата, Период) приводит дату к началу периода: ДЕНЬ, НЕДЕЛЯ, МЕСЯЦ, КВАРТАЛ, ГОД и других. Так группируют продажи по месяцам. ДАТАВРЕМЯ в языке запросов принимает только константы, а НачалоМесяца — функция встроенного языка, в запросе её нет.

  2. #oc_q_functions2 / 5
    Что вернёт РАЗНОСТЬДАТ(&Н, &К, ДЕНЬ) для &Н = 01.03 23:00 и &К = 02.03 01:00?
    A)Ошибку: разные даты
    B)0
    C)2
    D)1
    показать ответ и разбор
    +D)1

    // разбор: РАЗНОСТЬДАТ с периодом ДЕНЬ считает, сколько границ суток пересечено между датами, а не полные 24 часа. Между 1 марта 23:00 и 2 марта 01:00 одна граница — получится 1, хотя прошло два часа. Для точного интервала берут СЕКУНДА и делят.

  3. #oc_q_functions3 / 5
    Что делает ВЫБОР КОГДА Т.Сумма > 1000 ТОГДА "крупный" ИНАЧЕ "мелкий" КОНЕЦ?
    A)Группирует строки по сумме в две группы
    B)Отбирает строки с суммой больше 1000
    C)Сортирует строки по сумме, крупные первыми
    D)Вычисляет значение по условию для строки
    показать ответ и разбор
    +D)Вычисляет значение по условию для строки

    // разбор: ВЫБОР — условное выражение, аналог CASE: возвращает значение первой подходящей ветки, иначе ветку ИНАЧЕ. Без ИНАЧЕ для неподходящих строк вернётся NULL. Строки он не отбирает, это делает ГДЕ.

  4. #oc_q_functions4 / 5
    Как в запросе оставить только движения, где регистратор — документ «Реализация»?
    A)ТИП(Р.Регистратор) = Документ.Реализация
    B)Р.Регистратор = "Реализация"
    C)Р.Регистратор ССЫЛКА Документ.Реализация
    D)Р.Регистратор.ВидДокумента = "Реализация"
    показать ответ и разбор
    +C)`Р.Регистратор ССЫЛКА Документ.Реализация`

    // разбор: Оператор ССЫЛКА проверяет, что значение составного поля — ссылка указанного типа. Эквивалентно ТИПЗНАЧЕНИЯ(Р.Регистратор) = ТИП(Документ.Реализация). Сравнение со строкой и обращение к несуществующему полю ВидДокумента не работают.

  5. #oc_q_functions5 / 5
    Зачем в запросе выбирать ПРЕДСТАВЛЕНИЕ(Т.Контрагент), если в коде можно вывести саму ссылку?
    A)Чтобы получить ИНН контрагента в том же поле
    B)Без запроса за каждой ссылкой
    C)Чтобы обойти права доступа к справочнику
    D)Ссылку нельзя вывести в отчёт
    показать ответ и разбор
    +B)Без запроса за каждой ссылкой

    // разбор: При выводе ссылки платформа получает её представление отдельным обращением к базе, и на тысячах строк это тысячи мелких запросов. ПРЕДСТАВЛЕНИЕ в запросе получает текст сразу, одним соединением. Для ссылочных полей в СКД платформа делает это сама.

дальше

Теорию прочитал. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, бесплатная часть практики — одна сессия дня в сутки.