В практике разработки на платформе 1С часто возникает задача: получить список документов, табличная часть которых полностью соответствует заданному критерию. Типовой пример — выборка медицинских листов обследования, где пациент должен пройти всех врачей (у всех строк установлен признак Сдал), или отбор заказов, где все позиции полностью отгружены.
Сложность заключается в том, что обычное условие ГДЕ ТЧ.Поле = Истина выберет документы, в которых есть хотя бы одна такая строка, но проигнорирует наличие «плохих» строк в том же документе. В этой статье мы подробно разберем несколько методов решения этой задачи, проанализируем их производительность и рассмотрим сложные случаи.
Это самый популярный и интуитивно понятный способ. Его суть заключается в том, чтобы выбрать все документы, за исключением тех, в которых есть хотя бы одна «неправильная» строка. Рассмотрим ситуацию, когда нам нужны документы ЛистОбследования, где поле Сдал в табличной части тчПрофосмотр равно Истина абсолютно во всех записях.
Разберем логику построения такого запроса по шагам:
ГДЕ добавляем условие, исключающее ссылки, которые встречаются в выборке «невыполненных» строк.Пример реализации:
ВЫБРАТЬ
ЛистОбследования.Ссылка КАК Ссылка,
ЛистОбследования.Номер КАК Номер,
ЛистОбследования.Пациент КАК Пациент
ИЗ
Документ.ЛистОбследования КАК ЛистОбследования
ГДЕ
ЛистОбследования.Проведен = ИСТИНА
И НЕ ЛистОбследования.Ссылка В (
ВЫБРАТЬ
ТЧ.Ссылка
ИЗ
Документ.ЛистОбследования.тчПрофосмотр КАК ТЧ
ГДЕ
ТЧ.Сдал = ЛОЖЬ
)
Проанализируем этот код: подзапрос находит все ссылки на документы, где хотя бы один врач не пройден (Сдал = ЛОЖЬ). Основной запрос берет все документы и отсекает те, что попали в этот «черный список». Это надежный метод, который корректно обрабатывает документы с разным количеством строк.
В языке запросов 1С булевы значения сравнимы: ЛОЖЬ всегда меньше, чем ИСТИНА. Эту особенность можно использовать для эффективной группировки. Если минимальное значение поля Сдал в документе равно ИСТИНА, значит, в нем физически не может быть ни одной строки со значением ЛОЖЬ.
Рассмотрим пример запроса с группировкой:
ВЫБРАТЬ
ТЧ.Ссылка КАК Ссылка
ИЗ
Документ.ЛистОбследования.тчПрофосмотр КАК ТЧ
СГРУППИРОВАТЬ ПО
ТЧ.Ссылка
ИМЕЮЩИЕ
МИНИМУМ(ТЧ.Сдал) = ИСТИНА
Этот метод лаконичен и часто работает быстрее, так как не требует вложенных конструкций В (...). Однако стоит помнить, что при таком подходе мы работаем напрямую с табличной частью. Чтобы получить данные из шапки документа, этот запрос следует использовать как временную таблицу или вложить его в основной.
Еще один аналитический способ — сравнить общее количество строк в документе с количеством строк, удовлетворяющих условию. Если эти числа равны, значит, все строки в документе «хорошие».
Посмотрим на реализацию через конструкцию ВЫБОР:
ВЫБРАТЬ
ТЧ.Ссылка КАК Ссылка
ИЗ
Документ.ЛистОбследования.тчПрофосмотр КАК ТЧ
СГРУППИРОВАТЬ ПО
ТЧ.Ссылка
ИМЕЮЩИЕ
СУММА(ВЫБОР КОГДА ТЧ.Сдал ТОГДА 1 ИНАЧЕ 0 КОНЕЦ) = КОЛИЧЕСТВО(*)
Здесь мы суммируем единицы только для тех строк, где Сдал = ИСТИНА, и сравниваем результат с общим числом строк в этой группе (документе). Этот метод удобен, если условие более сложное, чем простое булево поле.
Иногда бизнес-логика требует более тонкой настройки. Например, нужно, чтобы все врачи в списке имели отметку Сдал = Истина, но конкретный специалист (например, Профпатолог) должен иметь Сдал = Ложь, так как он закрывает лист последним и на момент проверки его статус должен быть отрицательным.
В этом случае мы модифицируем метод исключения (Метод 1). Мы должны исключить документ, если в нем есть хотя бы одно нарушение любого из правил. Правила нарушения будут следующими:
Сдал = ЛОЖЬ.Сдал = ИСТИНА (если по логике задачи он должен быть еще не сдан).Проанализируем пример кода для такой задачи:
ВЫБРАТЬ
Лист.Ссылка
ИЗ
Документ.ЛистОбследования КАК Лист
ГДЕ
НЕ Лист.Ссылка В (
ВЫБРАТЬ
ТЧ.Ссылка
ИЗ
Документ.ЛистОбследования.тчПрофосмотр КАК ТЧ
ГДЕ
(ТЧ.Номенклатура <> &Профпатолог И ТЧ.Сдал = ЛОЖЬ)
ИЛИ (ТЧ.Номенклатура = &Профпатолог И ТЧ.Сдал = ИСТИНА)
)
Такой подход позволяет гибко настраивать требования к «идеальному» документу, описывая в подзапросе только те ситуации, которые нас не устраивают.
При работе с огромными базами данных конструкция НЕ Ссылка В (...) может приводить к замедлению, так как СУБД иногда неэффективно строит план запроса для оператора NOT IN. Профессиональные разработчики часто заменяют его левым соединением с проверкой на NULL.
Разберем этот механизм:
ЕСТЬ NULL (т.е. соединение не нашло ни одного нарушения).
ВЫБРАТЬ
Лист.Ссылка
ИЗ
Документ.ЛистОбследования КАК Лист
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ РАЗЛИЧНЫЕ
ТЧ.Ссылка КАК Ссылка
ИЗ
Документ.ЛистОбследования.тчПрофосмотр КАК ТЧ
ГДЕ
ТЧ.Сдал = ЛОЖЬ
) КАК ПлохиеСтроки
ПО Лист.Ссылка = ПлохиеСтроки.Ссылка
ГДЕ
ПлохиеСтроки.Ссылка ЕСТЬ NULL
Этот способ считается наиболее производительным на СУБД MS SQL Server и PostgreSQL при работе с миллионами записей.
При использовании вышеперечисленных методов следует учитывать два важных момента:
1. Пустые табличные части. Если в документе вообще нет строк, то подзапрос «плохих строк» ничего не вернет, и документ попадет в итоговую выборку. Если по логике системы документ без строк не считается «валидным», необходимо добавить проверку на наличие хотя бы одной строки через ВНУТРЕННЕЕ СОЕДИНЕНИЕ с табличной частью или условие КОЛИЧЕСТВО > 0.
2. Использование регистров. Если проверка «все ли строки заполнены» выполняется очень часто (например, в динамическом списке списка документов), это создаст высокую нагрузку на сервер, так как табличные части документов не индексируются по произвольным полям вроде Сдал. Рекомендуется при проведении документа записывать статус готовности в отдельный Регистр сведений. В таком случае запрос станет максимально простым и быстрым, обращаясь всего к одному индексированному полю ресурса регистра.
Мы рассмотрели основные способы получения документов с полностью валидными табличными частями. Выбор конкретного метода зависит от объема ваших данных и сложности условий, но для большинства повседневных задач метод с НЕ Ссылка В (ПОДЗАПРОС) является оптимальным балансом между читаемостью кода и скоростью разработки.