Работа с датами в 1С:Предприятие часто становится источником ошибок, особенно когда речь идет о сравнении с пустыми значениями. Пустая дата ('00010101' в формате 1С) — это не то же самое, что NULL в SQL, и неправильное обращение с ней может привести к некорректной выборке, ошибкам выполнения или даже падению производительности. В этой статье разберем, как грамотно строить запросы для сравнения с пустыми датами, какие подводные камни ожидают разработчиков, и как избежать типичных ошибок.
Особенность пустых дат в 1С заключается в их двойственной природе: с одной стороны, это полноценное значение типа Дата, с другой — оно часто используется как аналог отсутствия данных. Например, в справочниках пустая дата может означать, что запись никогда не обновлялась, а в документах — что поле не заполнено. При этом в запросах 1С пустая дата ведет себя иначе, чем в выражениях на встроенном языке, что создает дополнительные сложности.
Статья будет полезна как начинающим разработчикам, которые только осваивают работу с запросами, так и опытным специалистам, столкнувшимся с необходимостью оптимизировать сложные выборки. Мы рассмотрим не только базовый синтаксис, но и нюансы работы с разными СУБД (MS SQL, PostgreSQL, файловая база), а также альтернативные подходы для ускорения запросов.
1. Что такое пустая дата в 1С и как она хранится
В 1С:Предприятие пустая дата представлена значением '00010101' (1 января 1 года). Это не случайное число, а исторически сложившийся стандарт, унаследованный от ранних версий платформы. Важно понимать, что:
- 📅 Пустая дата — это не NULL. В базе данных она хранится как реальная дата, а не как отсутствие значения.
- 🔍 В запросах 1С пустая дата сравнивается как обычная дата, но с оговорками (см. следующий раздел).
- 💾 В разных СУБД пустая дата может интерпретироваться по-разному. Например, в MS SQL она хранится как
0001-01-01, а в PostgreSQL — как0001-01-01 BC(до нашей эры). - ⚡ В файловой базе 1С (для небольших конфигураций) пустая дата обрабатывается быстрее, чем в клиент-серверном варианте.
При работе с пустыми датами в коде (вне запросов) часто используют конструкции вроде Дата = '00010101' или функцию ЗначениеЗаполнено(). Однако в запросах такие приемы не всегда применимы. Например, выражение ГДЕ Дата = '00010101' сработает, но может быть неоптимальным с точки зрения производительности.
2. Базовый синтаксис сравнения с пустой датой в запросах
Самый простой способ сравнить поле с пустой датой в запросе — использовать прямое сравнение:
ВЫБРАТЬ
Справочник.Контрагенты.Наименование,
Справочник.Контрагенты.ДатаРегистрации
ИЗ
Справочник.Контрагенты КАК Справочник.Контрагенты
ГДЕ
Справочник.Контрагенты.ДатаРегистрации = ДАТАВРЕМЯ(1, 1, 1)
Однако этот подход имеет несколько нюансов:
- ⏱️ Производительность: В больших базах прямое сравнение с
'00010101'может быть медленнее, чем альтернативные методы (см. раздел 5). - 🔄 Кросс-платформенность: В некоторых СУБД (например, Oracle) может потребоваться явное приведение типов.
- 📝 Читаемость кода: Конструкция
ДАТАВРЕМЯ(1, 1, 1)менее наглядна, чем специализированные функции.
Альтернативный вариант — использование функции ЗНАЧЕНИЕЗАПОЛНЕНО() прямо в запросе:
ВЫБРАТЬ
Документ.ЗаказыПокупателей.Номер,
Документ.ЗаказыПокупателей.ДатаОтгрузки
ИЗ
Документ.ЗаказыПокупателей КАК Документ.ЗаказыПокупателей
ГДЕ
НЕ ЗНАЧЕНИЕЗАПОЛНЕНО(Документ.ЗаказыПокупателей.ДатаОтгрузки)
Этот способ более универсален, так как работает одинаково и для дат, и для других типов данных (ссылки, числа и т.д.). Однако он может быть менее эффективным на больших выборках.
Если вам нужно проверить несколько полей на пустоту, используйте конструкцию ГДЕ НЕ ЗНАЧЕНИЕЗАПОЛНЕНО(Поле1) И НЕ ЗНАЧЕНИЕЗАПОЛНЕНО(Поле2) — это сократит количество строк в результате на ранних этапах выполнения запроса.
3. Сравнение с NULL vs. сравнение с пустой датой
Многие разработчики путают пустую дату ('00010101') и NULL — отсутствие значения в базе данных. Это принципиально разные вещи:
| Критерий | Пустая дата ('00010101') |
NULL |
|---|---|---|
| Тип данных | Дата (со значением) | Отсутствие значения |
| Хранение в базе | Занимает место, хранится как дата | Не занимает место (в большинстве СУБД) |
| Сравнение в запросах | = ДАТАВРЕМЯ(1,1,1) |
ЕСТЬ NULL или Поле ЕСТЬ NULL |
| Производительность | Может использовать индексы | Требует специальных индексов (в некоторых СУБД) |
| Применение | Когда пустота — это осмысленное значение | Когда данные действительно отсутствуют |
В 1С поля редко бывают NULL — платформа обычно инициализирует их пустыми значениями по умолчанию. Однако в некоторых случаях (например, при ручном SQL-запросе или работе с внешними базами) NULL может встречаться. Для проверки на NULL в запросах 1С используется конструкция:
ВЫБРАТЬ
РегистрСведений.ЦеныНоменклатуры.Номенклатура,
РегистрСведений.ЦеныНоменклатуры.Цена
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК РегистрСведений.ЦеныНоменклатуры
ГДЕ
РегистрСведений.ЦеныНоменклатуры.ДатаОкончания ЕСТЬ NULL
⚠️ Внимание: В файловой базе 1С (например, в конфигурациях для небольших предприятий) поля никогда не бываютNULL— вместо этого они содержат пустые значения по умолчанию. ИспользованиеЕСТЬ NULLв таких базах приведет к пустому результату.
4. Типичные ошибки при работе с пустыми датами
Даже опытные разработчики иногда допускают ошибки при сравнении с пустыми датами. Вот наиболее распространенные:
- Использование неверного формата даты:
Ошибка:
ГДЕ Дата = '01.01.0001'(неправильный формат).
Правильно:
ГДЕ Дата = ДАТАВРЕМЯ(1, 1, 1)илиГДЕ Дата = '00010101'. - Путаница между пустой датой и NULL:
Ошибка:
ГДЕ Дата = NULL(так сравнивать нельзя).
Правильно:
ГДЕ Дата ЕСТЬ NULL(если поле действительно может быть NULL) илиГДЕ Дата = '00010101'. - Неучет временной части:
Ошибка: Сравнение даты с временем (
ДатаВремя) с пустой датой без учета времени.
Правильно: Использовать
НАЧАЛОДНЯ(Дата)или явное приведение типов. - Игнорирование регистрочувствительности:
Ошибка: В некоторых СУБД (например, PostgreSQL) сравнение
'00010101' = '0001-01-01'может не сработать из-за разного формата.
Правильно: Использовать функции приведения типов или универсальный формат.
Еще одна распространенная проблема — неявное приведение типов. Например, если поле в базе имеет тип ДатаВремя, а вы сравниваете его с '00010101' (которое интерпретируется как Дата), платформа может автоматически преобразовать значения, что приведет к неожиданным результатам. Чтобы избежать этого, явным образом приводите типы:
ГДЕ ДАТАВРЕМЯ(ГОД(Поле), МЕСЯЦ(Поле), ДЕНЬ(Поле)) = '00010101'
⚠️ Внимание: В 1С:Предприятие 8.3.20+ изменилось поведение функцииЗНАЧЕНИЕЗАПОЛНЕНО()для полей с типомДатаВремя. Если ваша конфигурация работает на более ранних версиях, тестируйте запросы на актуальной платформе.
5. Оптимизация запросов с пустыми датами
Сравнение с пустыми датами может существенно замедлять выполнение запросов, особенно на больших базах. Вот несколько способов оптимизации:
- 🚀 Используйте индексы: Если поле часто проверяется на пустоту, добавьте индекс по этому полю. В 1С это можно сделать через конфигуратор или прямо в запросе (для временных таблиц).
- ⚡ Заменяйте функции на прямые сравнения:
ЗНАЧЕНИЕЗАПОЛНЕНО()менее эффективен, чем= '00010101', если вам нужно только проверить пустоту даты. - 🔄 Разбивайте сложные условия: Вместо
ГДЕ (Дата = '00010101' ИЛИ Дата > ТЕКУЩАЯДАТА())используйте два отдельных запроса с объединением результатов (ОБЪЕДИНИТЬ). - 📊 Используйте временные таблицы: Для сложных отчетов предварительно отфильтруйте данные во временной таблице, а затем работайте с ней.
Пример оптимизированного запроса:
ВЫБРАТЬ
Документ.РеализацияТоваровУслуг.Номер КАК Номер,
Документ.РеализацияТоваровУслуг.Дата КАК Дата
ИЗ
Документ.РеализацияТоваровУслуг КАК Документ.РеализацияТоваровУслуг
ГДЕ
Документ.РеализацияТоваровУслуг.ДатаОплаты = '00010101'
И Документ.РеализацияТоваровУслуг.Дата МЕЖДУ &НачалоПериода И &КонецПериода
УПОРЯДОЧИТЬ ПО
Дата
Если запрос все равно выполняется медленно, рассмотрите возможность денормализации данных — добавьте в таблицу дополнительное поле-признак (например, ЕстьДатаОплаты типа Булево), которое будет обновляться триггером. Это позволит фильтровать данные без сравнения с пустой датой.
Проверьте наличие индексов на полях с датами|Замените ЗНАЧЕНИЕЗАПОЛНЕНО() на прямое сравнение|Разбейте сложные условия на несколько запросов|Используйте временные таблицы для промежуточных результатов|Протестируйте запрос на реальных данных с EXPLAIN (для SQL-баз)
-->
6. Альтернативные подходы: когда прямые сравнения не подходят
Иногда прямые сравнения с пустой датой невозможны или неэффективны. В таких случаях можно использовать альтернативные методы:
- Функция
ЕПУСТОЕЗНАЧЕНИЕ()(для 1С 8.3.14+):Эта функция возвращает
Истина, если значение пустое (в том числе для дат). Пример:ГДЕ ЕПУСТОЕЗНАЧЕНИЕ(Документ.Заказы.ДатаОтгрузки) - Использование
ВЫРАЗИТЬ()для приведения типов:Если поле имеет тип
ДатаВремя, но вам нужно сравнить только дату:ГДЕ ВЫРАЗИТЬ(Документ.Поступления.ДатаВремяДокумента КАК ДАТА) = '00010101' - Работа через виртуальные таблицы:
Для регистров сведений или накопления можно использовать виртуальные таблицы с отбором по пустым датам:
ВЫБРАТЬРегистрСведений.ОстаткиТоваровОрганизаций.Номенклатура,
РегистрСведений.ОстаткиТоваровОрганизаций.Количество
ИЗ
РегистрСведений.ОстаткиТоваровОрганизаций.Остатки(&ТекДата, )
КАК РегистрСведений.ОстаткиТоваровОрганизаций
ГДЕ
РегистрСведений.ОстаткиТоваровОрганизаций.ДатаПоступления = '00010101'
Для сложных отчетов, где требуется анализировать пустые даты, иногда целесообразно вынести логику в код на встроенном языке, а не пытаться сделать всё в одном запросе. Например:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Документ.ЗаказыПокупателей.Ссылка КАК Ссылка,
| Документ.ЗаказыПокупателей.ДатаОтгрузки КАК ДатаОтгрузки
|ИЗ
| Документ.ЗаказыПокупателей КАК Документ.ЗаказыПокупателей";
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
Если ЗначениеЗаполнено(Выборка.ДатаОтгрузки) Тогда
// Обработка непустых дат
Иначе
// Обработка пустых дат
КонецЕсли;
КонецЦикла;
Почему иногда лучше обрабатывать пустые даты в коде, а не в запросе?
В коде проще реализовать сложную логику (например, каскадные проверки нескольких полей). Кроме того, некоторые операции (например, рекурсивный обход связей) невозможно или крайне неэффективно реализовать чисто на языке запросов. Также код на встроенном языке легче отлаживать и модифицировать, особенно если бизнес-логика часто меняется.
7. Особенности работы в разных СУБД
Поведение пустых дат может отличаться в зависимости от используемой СУБД. Рассмотрим ключевые особенности для наиболее распространенных вариантов:
| СУБД | Формат хранения пустой даты | Особенности сравнения | Рекомендации |
|---|---|---|---|
| MS SQL Server | 0001-01-01 |
Сравнение работает корректно, но может быть медленным без индексов. | Используйте CONVERT(DATE, Поле) = '00010101' для явного приведения типов. |
| PostgreSQL | 0001-01-01 BC (до нашей эры) |
Может требовать явного указания эры (BC/AD). |
Проверяйте настройки локали сервера — они влияют на интерпретацию дат. |
| Файловая база (1С) | '00010101' (как строка) |
Сравнения работают быстро, но нет оптимизации по индексам. | Для больших баз рассмотрите переход на клиент-серверный вариант. |
| Oracle | Зависит от версии (может быть NULL) |
Требует явного приведения типов (TO_DATE). |
Используйте NVL(Поле, TO_DATE('00010101', 'YYYYMMDD')) для унификации. |
Для PostgreSQL и Oracle иногда приходится писать специфичные запросы. Например, в PostgreSQL для проверки пустой даты можно использовать:
ГДЕ Поле = DATE '0001-01-01 BC'
А в Oracle может потребоваться:
ГДЕ Поле = TO_DATE('00010101', 'YYYYMMDD')
⚠️ Внимание: При переносе конфигурации между разными СУБД всегда тестируйте запросы с пустыми датами — их поведение может кардинально отличаться. Например, запрос, работающий в MS SQL, может вернуть пустой результат в PostgreSQL из-за разницы в хранении дат.
8. Практические примеры: от простых к сложным
Рассмотрим несколько реальных примеров использования сравнения с пустыми датами в 1С:
- Поиск неоплаченных заказов:
Задача: Найти заказы, у которых не указана дата оплаты.
ВЫБРАТЬЗаказы.Номер,
Заказы.Дата,
Заказы.Сумма
ИЗ
Документ.ЗаказПокупателя КАК Заказы
ГДЕ
Заказы.ДатаОплаты = '00010101'
И Заказы.Дата МЕЖДУ &НачалоПериода И &КонецПериода
- Анализ просроченных задач:
Задача: Найти задачи, у которых не указана дата выполнения, но установлен дедлайн.
ВЫБРАТЬЗадачи.Описание,
Задачи.Дедлайн
ИЗ
Документ.Задачи КАК Задачи
ГДЕ
Задачи.ДатаВыполнения = '00010101'
И Задачи.Дедлайн < ТЕКУЩАЯДАТА()
- Сверка данных с внешней системой:
Задача: Найти записи, которые не были синхронизированы (дата синхронизации пустая).
ВЫБРАТЬСправочник.Контрагенты.Наименование,
Справочник.Контрагенты.ИНН
ИЗ
Справочник.Контрагенты КАК Справочник.Контрагенты
ГДЕ
НЕ ЗНАЧЕНИЕЗАПОЛНЕНО(Справочник.Контрагенты.ДатаСинхронизации)
И Справочник.Контрагенты.ПометкаУдаления = ЛОЖЬ
- Отчет по документам без движения:
Задача: Сформировать отчет по документам, у которых не проведено движение (дата проведения пустая).
ВЫБРАТЬДокументы.ВидДокумента,
COUNT(*) КАК Количество
ИЗ
Документ.ЛюбойДокумент КАК Документы
ГДЕ
Документы.ДатаПроведения = '00010101'
СГРУППИРОВАТЬ ПО
Документы.ВидДокумента
Для сложных отчетов, где требуется анализировать пустые даты в связке с другими условиями, рекомендуется использовать композицию запросов. Например, сначала отобрать документы с пустыми датами, а затем присоединить к ним дополнительные данные:
ВЫБРАТЬ
ОсновнойЗапрос.Номер,
ОсновнойЗапрос.Дата,
ДополнительныеДанные.Комментарий
ИЗ
(ВЫБРАТЬ
Документ.Заказы.Номер,
Документ.Заказы.Дата
ИЗ
Документ.Заказы КАК Документ.Заказы
ГДЕ
Документ.Заказы.ДатаОтгрузки = '00010101') КАК ОсновнойЗапрос
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КомментарииКДокументам КАК ДополнительныеДанные
ПО ОсновнойЗапрос.Номер = ДополнительныеДанные.НомерДокумента
Для сложных отчетов с пустыми датами всегда разбивайте задачу на несколько простых запросов. Это не только ускорит выполнение, но и сделает код более поддерживаемым.
FAQ: Частые вопросы по сравнению с пустыми датами
Можно ли использовать Поле = NULL для проверки пустой даты?
Нет, это грубая ошибка. NULL и пустая дата ('00010101') — это разные вещи. Для проверки на NULL используйте Поле ЕСТЬ NULL, а для пустой даты — Поле = '00010101' или НЕ ЗНАЧЕНИЕЗАПОЛНЕНО(Поле).
Почему запрос с пустой датой работает медленно?
Это может быть связано с:
- Отсутствием индексов на поле с датой.
- Использованием функций (например,
ЗНАЧЕНИЕЗАПОЛНЕНО()) вместо прямых сравнений. - Большим объемом данных — в этом случае поможет разбиение запроса или использование временных таблиц.
Проверьте план выполнения запроса (в SQL-базах) или используйте ПОЯСНИТЬ() в 1С.
Как сравнить поле типа ДатаВремя с пустой датой?
Есть два варианта:
- Привести поле к типу
Дата:ГДЕ ДАТАВРЕМЯ(ГОД(Поле), МЕСЯЦ(Поле), ДЕНЬ(Поле)) = '00010101' - Сравнить с пустым значением
ДатаВремя:ГДЕ Поле = ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0)
Что будет, если в базе есть реальные данные с датой 0001-01-01?
Это критическая проблема: платформа 1С не различает "пустую дату" как специальное значение и реальную дату 1 января 1 года. Если в вашей базе могут храниться реальные исторические данные с такой датой, никогда не используйте ее как признак пустоты. Вместо этого:
- Добавьте отдельное поле-признак (например,
ДатаУказанатипаБулево). - Используйте
NULL(если СУБД это поддерживает). - Выберите другую "магическую" дату (например,
'18991231'), но документируйте это решение.
Как проверить пустоту даты в отборе компоновки данных?
В отборе компоновки данных можно использовать:
- Для поля
Дата: установите условиеРавнои значение01.01.0001. - Для поля
ДатаВремя: используйте условиеРавнои значение01.01.0001 0:00:00. - Или добавьте вычисляемое поле с функцией
ЗНАЧЕНИЕЗАПОЛНЕНО()и отфильтруйте по нему.