При проектировании и доработке конфигураций на платформе «1С:Предприятие» разработчики нередко сталкиваются с ситуацией, когда несколько выбранных опций хранятся в базе данных в виде одной строки. Яркий пример — график доставки по дням недели, сохраненный в виде строки цифр (например, "13" означает доставку в понедельник и среду, а "136" — в понедельник, среду и субботу). Попытка получить текстовое представление этих дней простым запросом через классическую конструкцию ВЫБОР КОГДА приводит к тому, что система возвращает только один (первый совпавший) день или не возвращает ничего. В этой статье мы подробно разберем, почему так происходит, как решить эту задачу в рамках одного запроса и какие существуют архитектурно правильные альтернативы.
Рассмотрим исходный запрос, который часто пишут начинающие разработчики:
ВЫБОР
КОГДА ТорговыеТочки.ГрафикДоставки = "1" ТОГДА "Понедельник"
КОГДА ТорговыеТочки.ГрафикДоставки = "2" ТОГДА "Вторник"
КОГДА ТорговыеТочки.ГрафикДоставки = "3" ТОГДА "Среда"
...
КОНЕЦ КАК ДниДоставки
В этой конструкции кроются сразу две проблемы:
=) сравнивает строку целиком. Если в поле ГрафикДоставки записано значение "13", то проверка ГрафикДоставки = "1" вернет ложь. Проверка ГрафикДоставки = "3" также вернет ложь. В итоге запрос вообще вернет пустое значение (NULL).ВЫБОР КОГДА ... КОНЕЦ в SQL и в языке запросов 1С работает последовательно до первого истинного условия. Как только система находит условие, которое выполняется, она возвращает соответствующий результат и прекращает дальнейшие проверки в рамках этой конструкции. Вывести несколько разных значений из одной конструкции ВЫБОР физически невозможно.Для того чтобы вывести все выбранные дни недели в одну колонку, нам потребуется применить конкатенацию (сложение строк) нескольких независимых конструкций ВЫБОР.
Посмотрим на пример реализации, предложенный участниками форума. Вместо сравнения на строгое равенство мы будем использовать оператор поиска подстроки ПОДОБНО (LIKE в SQL) и складывать результаты работы отдельных блоков ВЫБОР. Каждый день недели анализируется в собственной независимой конструкции:
ВЫБРАТЬ
ТорговыеТочки.Район,
ТорговыеТочки.Участок,
ТорговыеТочки.Наименование КАК ТорговаяТочка,
ВЫБОР КОГДА ТорговыеТочки.ГрафикДоставки ПОДОБНО "%1%" ТОГДА "Понедельник, " ИНАЧЕ "" КОНЕЦ +
ВЫБОР КОГДА ТорговыеТочки.ГрафикДоставки ПОДОБНО "%2%" ТОГДА "Вторник, " ИНАЧЕ "" КОНЕЦ +
ВЫБОР КОГДА ТорговыеТочки.ГрафикДоставки ПОДОБНО "%3%" ТОГДА "Среда, " ИНАЧЕ "" КОНЕЦ +
ВЫБОР КОГДА ТорговыеТочки.ГрафикДоставки ПОДОБНО "%4%" ТОГДА "Четверг, " ИНАЧЕ "" КОНЕЦ +
ВЫБОР КОГДА ТорговыеТочки.ГрафикДоставки ПОДОБНО "%5%" ТОГДА "Пятница, " ИНАЧЕ "" КОНЕЦ +
ВЫБОР КОГДА ТорговыеТочки.ГрафикДоставки ПОДОБНО "%6%" ТОГДА "Суббота, " ИНАЧЕ "" КОНЕЦ +
ВЫБОР КОГДА ТорговыеТочки.ГрафикДоставки ПОДОБНО "%7%" ТОГДА "Воскресенье" ИНАЧЕ "" КОНЕЦ КАК ДниДоставки
ИЗ
Справочник.ТорговыеТочки КАК ТорговыеТочки
Как это работает: запрос проверяет наличие символа "1" в строке графика. Если символ найден, к результирующей строке прибавляется "Понедельник, ". Затем аналогично проверяется "2", "3" и так далее. Все результаты склеиваются в одно длинное текстовое поле.
При всей простоте у этого метода есть существенные недостатки, о которых необходимо знать профессиональному разработчику.
Применение данного метода на реальных базах данных может повлечь за собой серьезные технические проблемы:
"%1%") делает невозможным использование индексов СУБД по полю ГрафикДоставки. База данных будет вынуждена сканировать всю таблицу справочника целиком для выполнения запроса. Если в вашей базе десятки тысяч торговых точек, такой запрос приведет к долгому ожиданию формирования отчета."13"), то в результате сложения строк мы получим: "Понедельник, Среда, ". На конце строки останется висящая запятая и пробел. Убрать ее средствами чистого SQL-запроса в 1С крайне громоздко (придется писать сложные функции анализа длины строки).ГрафикДоставки окажется незаполненным (содержит NULL), то сложение строк в некоторых СУБД (например, PostgreSQL) может вернуть пустой результат для всей строки. Чтобы этого избежать, придется оборачивать обращения к полям в функцию ЕСТЬNULL.Для исключения проблем с производительностью СУБД и получения красивого форматирования строк правильнее перенести задачу склеивания дней недели на сторону встроенного языка 1С. Запрос должен получать только сырые данные (строку цифр), а код — преобразовывать ее в читаемый вид.
Разберем по шагам создание функции форматирования:
// Функция преобразует строку вида "136" в "Понедельник, Среда, Суббота"
Функция ПолучитьПредставлениеГрафика(СтрокаЦифр) Экспорт
Если ПустаяСтрока(СтрокаЦифр) Тогда
Возврат "";
КонецЕсли;
// Заполняем соответствие дней недели
СоответствиеДней = Новый Соответствие;
СоответствиеДней.Вставить("1", "Понедельник");
СоответствиеДней.Вставить("2", "Вторник");
СоответствиеДней.Вставить("3", "Среда");
СоответствиеДней.Вставить("4", "Четверг");
СоответствиеДней.Вставить("5", "Пятница");
СоответствиеДней.Вставить("6", "Суббота");
СоответствиеДней.Вставить("7", "Воскресенье");
МассивВыбранныхДней = Новый Массив;
// Обходим каждый символ входящей строки
Для Индекс = 1 По СтрДлина(СтрокаЦифр) Цикл
Символ = Сред(СтрокаЦифр, Индекс, 1);
ДеньНедели = СоответствиеДней.Получить(Символ);
Если ДеньНедели <> Неопределено Тогда
МассивВыбранныхДней.Добавить(ДеньНедели);
КонецЕсли;
КонецЦикла;
// Склеиваем массив через запятую БЕЗ висящего знака на конце
Возврат СтрСоединить(МассивВыбранныхДней, ", ");
КонецФункции
При обходе выборки результата запроса мы просто передаем значение реквизита в эту функцию и выводим в отчет красивую и чистую строку без лишних запятых.
Если ваш отчет строится с использованием Системы компоновки данных (СКД), то вы можете элегантно совместить легкий запрос и форматирование кода. Рассмотрим, как это сделать:
ПолучитьПредставлениеГрафика в общий модуль конфигурации (например, РаботаСКлиентамиПереопределяемый). Убедитесь, что у модуля стоят флаги "Клиент", "Сервер" и "Вызов сервера".ПредставлениеГрафика.РаботаСКлиентамиПереопределяемый.ПолучитьПредставлениеГрафика(ГрафикДоставки).ГрафикДоставки.СУБД моментально выберет простые строки из базы данных, а процессор компоновки данных быстро преобразует их в человеческий вид перед выводом пользователю.
Хранение списка значений в одном текстовом поле (строка типа "137") — это классический антипаттерн проектирования реляционных баз данных. Данные в таком виде невозможно быстро фильтровать, индексировать и анализировать. Если конфигурация находится в вашей разработке и ее структуру можно менять, рассмотрите один из правильных вариантов рефакторинга:
ТорговыеТочки табличную часть ГрафикДоставки с одним реквизитом ДеньНедели (тип: ПеречислениеСсылка.ДниНедели или Число). В запросах вы сможете легко соединяться с этой табличной частью, группировать данные или накладывать быстрые фильтры (например, "найти все точки с доставкой по средам").ГрафикиДоставкиТорговыхТочек с измерениями ТорговаяТочка и ДеньНедели. Этот вариант обеспечивает максимальное быстродействие СУБД, так как платформа автоматически строит эффективные индексы по измерениям регистров.Булево (ДоставкаВПонедельник, ДоставкаВоВторник и т.д.). Запросы по булевым полям выполняются СУБД очень быстро и не требуют текстового разбора масок.Какое бы решение вы ни выбрали, всегда ориентируйтесь на объем базы данных и требования к производительности системы. Для простых и редких отчетов подойдет решение с конкатенацией ПОДОБНО, для крупных учетных систем — обработка через функции общего модуля или рефакторинг структуры хранения данных.