Разбор строки с разделителями в запросе: номенклатура по списку артикулов в УТ 11.5.27.70, КА 2.5.27.93 и БП 3.0.203.18
Список артикулов приходит одной строкой через запятую: из Excel, из письма, от менеджера. Строка разбирается прямо в запросе, без цикла и без заполнения таблицы в коде, а пробелы в начале артикулов в справочнике не мешают поиску. Сверено со встроенным разбором и независимым поиском на трех базах.
Задача
Менеджер присылает строку "А-100, А-101, Б-7" и спрашивает, что это за позиции. Обычно строку разбирают в коде функцией СтрРазделить, заполняют таблицу значений и передают ее в запрос параметром. Это работает, но в консоли запросов, в отчете на СКД или в обработке без кода так не сделать. Нужен разбор средствами самого запроса.
Решение
Строка дополняется разделителем в начале и в конце, чтобы каждый элемент стоял между двумя разделителями. Таблица цифр дает номера позиций символов, по функции ПОДСТРОКА отбираются позиции, где стоит разделитель. Для каждого разделителя соединением с той же таблицей находится ближайший следующий, между ними лежит один элемент. Элемент вырезается функцией ПОДСТРОКА, пробелы по краям убираются, пустые элементы и повторы отбрасываются.
Найденные артикулы соединяются со справочником номенклатуры. Артикул справочника сравнивается без пробелов по краям: в реальных базах пробел в начале артикула встречается часто, и простое равенство такую позицию не находит. Не найденные артикулы остаются в результате с пометкой.
Проверено на трех базах строкой из реальных артикулов с ловушками: лишние пробелы, повтор, пустой элемент, несуществующий артикул. Разбор совпал со встроенной функцией, найденные позиции совпали с независимым поиском, пропусков нет.
// Номенклатура по списку артикулов, вставленному одной строкой через разделитель, // например из Excel: "А-100, А-101, Б-7". Строка разбирается прямо в запросе: // без цикла в коде и без временной таблицы, заполняемой из встроенного языка. // 1. Цифры от 0 до 9: из них собираются номера дней без таблиц календаря. ВЫБРАТЬ 0 КАК Цифра ПОМЕСТИТЬ втЦифры ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 9 ; // 2. Номера позиций символов строки. Строка дополняется разделителем в начале и в конце, // чтобы каждый элемент стоял между двумя разделителями. ВЫБРАТЬ 1 + Единицы.Цифра + 10 * Десятки.Цифра + 100 * Сотни.Цифра + 1000 * Тысячи.Цифра КАК Позиция ПОМЕСТИТЬ втПозиции ИЗ втЦифры КАК Единицы, втЦифры КАК Десятки, втЦифры КАК Сотни, втЦифры КАК Тысячи ГДЕ 1 + Единицы.Цифра + 10 * Десятки.Цифра + 100 * Сотни.Цифра + 1000 * Тысячи.Цифра <= ДЛИНАСТРОКИ(&Разделитель + &Список + &Разделитель) ; // 3. Позиции, на которых стоит разделитель. ВЫБРАТЬ Позиции.Позиция КАК Позиция ПОМЕСТИТЬ втРазделители ИЗ втПозиции КАК Позиции ГДЕ ПОДСТРОКА(&Разделитель + &Список + &Разделитель, Позиции.Позиция, 1) = &Разделитель ; // 4. Для каждого разделителя ближайший следующий: между ними лежит один элемент. ВЫБРАТЬ Левый.Позиция КАК Начало, МИНИМУМ(Правый.Позиция) КАК Конец ПОМЕСТИТЬ втОтрезки ИЗ втРазделители КАК Левый ВНУТРЕННЕЕ СОЕДИНЕНИЕ втРазделители КАК Правый ПО (Правый.Позиция > Левый.Позиция) СГРУППИРОВАТЬ ПО Левый.Позиция ; // 5. Элементы списка без пробелов по краям, пустые отбрасываются. Результат приводится // к строке ограниченной длины: строки неограниченной длины в запросе сравнивать нельзя, // поэтому пустые элементы отсекаются по длине, а не сравнением с пустой строкой. ВЫБРАТЬ ВЫРАЗИТЬ(СОКРЛП(ПОДСТРОКА(&Разделитель + &Список + &Разделитель, Отрезки.Начало + 1, Отрезки.Конец - Отрезки.Начало - 1)) КАК СТРОКА(50)) КАК Артикул, МИНИМУМ(Отрезки.Начало) КАК Порядок ПОМЕСТИТЬ втАртикулы ИЗ втОтрезки КАК Отрезки ГДЕ ДЛИНАСТРОКИ(СОКРЛП(ПОДСТРОКА(&Разделитель + &Список + &Разделитель, Отрезки.Начало + 1, Отрезки.Конец - Отрезки.Начало - 1))) > 0 СГРУППИРОВАТЬ ПО ВЫРАЗИТЬ(СОКРЛП(ПОДСТРОКА(&Разделитель + &Список + &Разделитель, Отрезки.Начало + 1, Отрезки.Конец - Отрезки.Начало - 1)) КАК СТРОКА(50)) ИНДЕКСИРОВАТЬ ПО Артикул ; // 6. Каждый артикул из списка и найденная по нему номенклатура. Не найденные // артикулы остаются в результате с пустой номенклатурой. Артикул справочника // сравнивается без пробелов по краям: пробел в начале артикула встречается часто, // и простое равенство такую позицию не находит. ВЫБРАТЬ Артикулы.Порядок КАК Порядок, Артикулы.Артикул КАК Артикул, Номенклатура.Ссылка КАК Номенклатура, ВЫБОР КОГДА Номенклатура.Ссылка ЕСТЬ NULL ТОГДА "не найден" ИНАЧЕ &ПустаяСтрока КОНЕЦ КАК Примечание ИЗ втАртикулы КАК Артикулы ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура ПО (СОКРЛП(Номенклатура.Артикул) = Артикулы.Артикул) И (НЕ Номенклатура.ПометкаУдаления) УПОРЯДОЧИТЬ ПО Порядок
// Номенклатура по списку артикулов, вставленному одной строкой через разделитель, // например из Excel: "А-100, А-101, Б-7". Строка разбирается прямо в запросе: // без цикла в коде и без временной таблицы, заполняемой из встроенного языка. // 1. Цифры от 0 до 9: из них собираются номера дней без таблиц календаря. ВЫБРАТЬ 0 КАК Цифра ПОМЕСТИТЬ втЦифры ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 9 ; // 2. Номера позиций символов строки. Строка дополняется разделителем в начале и в конце, // чтобы каждый элемент стоял между двумя разделителями. ВЫБРАТЬ 1 + Единицы.Цифра + 10 * Десятки.Цифра + 100 * Сотни.Цифра + 1000 * Тысячи.Цифра КАК Позиция ПОМЕСТИТЬ втПозиции ИЗ втЦифры КАК Единицы, втЦифры КАК Десятки, втЦифры КАК Сотни, втЦифры КАК Тысячи ГДЕ 1 + Единицы.Цифра + 10 * Десятки.Цифра + 100 * Сотни.Цифра + 1000 * Тысячи.Цифра <= ДЛИНАСТРОКИ(&Разделитель + &Список + &Разделитель) ; // 3. Позиции, на которых стоит разделитель. ВЫБРАТЬ Позиции.Позиция КАК Позиция ПОМЕСТИТЬ втРазделители ИЗ втПозиции КАК Позиции ГДЕ ПОДСТРОКА(&Разделитель + &Список + &Разделитель, Позиции.Позиция, 1) = &Разделитель ; // 4. Для каждого разделителя ближайший следующий: между ними лежит один элемент. ВЫБРАТЬ Левый.Позиция КАК Начало, МИНИМУМ(Правый.Позиция) КАК Конец ПОМЕСТИТЬ втОтрезки ИЗ втРазделители КАК Левый ВНУТРЕННЕЕ СОЕДИНЕНИЕ втРазделители КАК Правый ПО (Правый.Позиция > Левый.Позиция) СГРУППИРОВАТЬ ПО Левый.Позиция ; // 5. Элементы списка без пробелов по краям, пустые отбрасываются. Результат приводится // к строке ограниченной длины: строки неограниченной длины в запросе сравнивать нельзя, // поэтому пустые элементы отсекаются по длине, а не сравнением с пустой строкой. ВЫБРАТЬ ВЫРАЗИТЬ(СОКРЛП(ПОДСТРОКА(&Разделитель + &Список + &Разделитель, Отрезки.Начало + 1, Отрезки.Конец - Отрезки.Начало - 1)) КАК СТРОКА(50)) КАК Артикул, МИНИМУМ(Отрезки.Начало) КАК Порядок ПОМЕСТИТЬ втАртикулы ИЗ втОтрезки КАК Отрезки ГДЕ ДЛИНАСТРОКИ(СОКРЛП(ПОДСТРОКА(&Разделитель + &Список + &Разделитель, Отрезки.Начало + 1, Отрезки.Конец - Отрезки.Начало - 1))) > 0 СГРУППИРОВАТЬ ПО ВЫРАЗИТЬ(СОКРЛП(ПОДСТРОКА(&Разделитель + &Список + &Разделитель, Отрезки.Начало + 1, Отрезки.Конец - Отрезки.Начало - 1)) КАК СТРОКА(50)) ИНДЕКСИРОВАТЬ ПО Артикул ; // 6. Каждый артикул из списка и найденная по нему номенклатура. Не найденные // артикулы остаются в результате с пустой номенклатурой. Артикул справочника // сравнивается без пробелов по краям: пробел в начале артикула встречается часто, // и простое равенство такую позицию не находит. ВЫБРАТЬ Артикулы.Порядок КАК Порядок, Артикулы.Артикул КАК Артикул, Номенклатура.Ссылка КАК Номенклатура, ВЫБОР КОГДА Номенклатура.Ссылка ЕСТЬ NULL ТОГДА "не найден" ИНАЧЕ &ПустаяСтрока КОНЕЦ КАК Примечание ИЗ втАртикулы КАК Артикулы ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура ПО (СОКРЛП(Номенклатура.Артикул) = Артикулы.Артикул) И (НЕ Номенклатура.ПометкаУдаления) УПОРЯДОЧИТЬ ПО Порядок
// Номенклатура по списку артикулов, вставленному одной строкой через разделитель, // например из Excel: "А-100, А-101, Б-7". Строка разбирается прямо в запросе: // без цикла в коде и без временной таблицы, заполняемой из встроенного языка. // 1. Цифры от 0 до 9: из них собираются номера дней без таблиц календаря. ВЫБРАТЬ 0 КАК Цифра ПОМЕСТИТЬ втЦифры ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 9 ; // 2. Номера позиций символов строки. Строка дополняется разделителем в начале и в конце, // чтобы каждый элемент стоял между двумя разделителями. ВЫБРАТЬ 1 + Единицы.Цифра + 10 * Десятки.Цифра + 100 * Сотни.Цифра + 1000 * Тысячи.Цифра КАК Позиция ПОМЕСТИТЬ втПозиции ИЗ втЦифры КАК Единицы, втЦифры КАК Десятки, втЦифры КАК Сотни, втЦифры КАК Тысячи ГДЕ 1 + Единицы.Цифра + 10 * Десятки.Цифра + 100 * Сотни.Цифра + 1000 * Тысячи.Цифра <= ДЛИНАСТРОКИ(&Разделитель + &Список + &Разделитель) ; // 3. Позиции, на которых стоит разделитель. ВЫБРАТЬ Позиции.Позиция КАК Позиция ПОМЕСТИТЬ втРазделители ИЗ втПозиции КАК Позиции ГДЕ ПОДСТРОКА(&Разделитель + &Список + &Разделитель, Позиции.Позиция, 1) = &Разделитель ; // 4. Для каждого разделителя ближайший следующий: между ними лежит один элемент. ВЫБРАТЬ Левый.Позиция КАК Начало, МИНИМУМ(Правый.Позиция) КАК Конец ПОМЕСТИТЬ втОтрезки ИЗ втРазделители КАК Левый ВНУТРЕННЕЕ СОЕДИНЕНИЕ втРазделители КАК Правый ПО (Правый.Позиция > Левый.Позиция) СГРУППИРОВАТЬ ПО Левый.Позиция ; // 5. Элементы списка без пробелов по краям, пустые отбрасываются. Результат приводится // к строке ограниченной длины: строки неограниченной длины в запросе сравнивать нельзя, // поэтому пустые элементы отсекаются по длине, а не сравнением с пустой строкой. ВЫБРАТЬ ВЫРАЗИТЬ(СОКРЛП(ПОДСТРОКА(&Разделитель + &Список + &Разделитель, Отрезки.Начало + 1, Отрезки.Конец - Отрезки.Начало - 1)) КАК СТРОКА(50)) КАК Артикул, МИНИМУМ(Отрезки.Начало) КАК Порядок ПОМЕСТИТЬ втАртикулы ИЗ втОтрезки КАК Отрезки ГДЕ ДЛИНАСТРОКИ(СОКРЛП(ПОДСТРОКА(&Разделитель + &Список + &Разделитель, Отрезки.Начало + 1, Отрезки.Конец - Отрезки.Начало - 1))) > 0 СГРУППИРОВАТЬ ПО ВЫРАЗИТЬ(СОКРЛП(ПОДСТРОКА(&Разделитель + &Список + &Разделитель, Отрезки.Начало + 1, Отрезки.Конец - Отрезки.Начало - 1)) КАК СТРОКА(50)) ИНДЕКСИРОВАТЬ ПО Артикул ; // 6. Каждый артикул из списка и найденная по нему номенклатура. Не найденные // артикулы остаются в результате с пустой номенклатурой. Артикул справочника // сравнивается без пробелов по краям: пробел в начале артикула встречается часто, // и простое равенство такую позицию не находит. ВЫБРАТЬ Артикулы.Порядок КАК Порядок, Артикулы.Артикул КАК Артикул, Номенклатура.Ссылка КАК Номенклатура, ВЫБОР КОГДА Номенклатура.Ссылка ЕСТЬ NULL ТОГДА "не найден" ИНАЧЕ &ПустаяСтрока КОНЕЦ КАК Примечание ИЗ втАртикулы КАК Артикулы ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура ПО (СОКРЛП(Номенклатура.Артикул) = Артикулы.Артикул) И (НЕ Номенклатура.ПометкаУдаления) УПОРЯДОЧИТЬ ПО Порядок
Что учесть
- Результат ПОДСТРОКА от параметра имеет неограниченную длину, а такие строки в запросе нельзя сравнивать. Поэтому элемент приводится к СТРОКА(50) функцией ВЫРАЗИТЬ, а пустые элементы отсекаются по ДЛИНАСТРОКИ, а не сравнением с пустой строкой.
- Запрос проверен на платформах 8.3.27 и 8.5. Если платформа не знает функцию ДЛИНАСТРОКИ, ограничьте номера позиций константой, например 4000.
- Длина строки до 9999 символов. Для длинных списков добавьте еще одну копию таблицы цифр.
- Разделитель задается одним символом. Для списка по строкам, как из столбца Excel, замените переводы строк на запятые до передачи или передайте символ перевода строки параметром.
- Если у одного артикула несколько позиций номенклатуры, в результате будут все: это повод проверить справочник.
- Сравнение без пробелов по краям не использует индекс по артикулу. На справочниках в сотни тысяч позиций это заметно, тогда лучше один раз очистить артикулы от пробелов.
Как воспользоваться
- Выберите вкладку своей конфигурации над кодом: под каждой лежит запрос, выполненный именно в этой конфигурации.
- Скачайте обработку под ней и откройте в 1С через "Файл, Открыть". Форма покажет поля отбора, результат появится в той же форме таблицей.
- Нужно иначе: скопируйте запрос кнопкой над кодом и подставьте в консоль запросов или в свою обработку. Все параметры перечислены в тексте, значения по умолчанию видны в обработке.
- Хотите то же самое, но под свою базу и свои поля: подключите базу к мосту и попросите ИИ доработать этот запрос. Он увидит структуру вашей конфигурации и проверит результат на ваших данных.
Комментарии
Нашли ошибку в коде, знаете, как лучше, или запрос повел себя иначе в вашей конфигурации: напишите. Комментарии открываются после проверки, на вопросы отвечаем здесь же. Реклама и ссылки на посторонние сайты не проходят.