Настоящая статья не претендует на полноту описания и подробный анализ ошибок и «подводных камней» встроенного языка платформы 1С:Предприятие 7.7. Первоначально описание делалось исключительно для себя после того, как несколько раз удалось наступить на одни и те же «грабли». В список включено только то, с чем приходилось сталкиваться мне лично (но не всё, с чем приходилось сталкиваться, попало в этот список). Надеюсь, этот опыт пригодится программистам, продолжающим работать на платформе 1С:Предприятие 7.7.
Проблемы пунктов 1–5 были обнаружены в релизе 1С:Предприятие 7.7.17 (формат базы DBF), 6–12 — в релизе 1С:Предприятие 7.7.25 (формат базы SQL), 13 — в релизе 1С:Предприятие 7.7.25 (оба формата базы), 14 — в релизе 1С:Предприятие 7.7.27 (оба формата базы), 15 — в релизе 1С:Предприятие 7.7.27 (формат базы SQL). Скорее всего, большинство этих проблем актуальны и для других релизов и форматов баз. Поскольку я уже практически не работаю на платформе 7.7, я проверил актуальность для последнего (27-го) релиза только части пунктов. Остальное предлагаю проверить всем желающим.
Надо иметь в виду, что ошибки в реализации запросов довольно коварны — они могут проявляться не только в зависимости от релиза платформы и формата базы, но и от типов значений переменных запроса, дополнительных условий, функций и т.п. В пункте 8, например, описана ситуация, когда ошибка наблюдается только при определённом порядке описаний внутренней переменной запроса. Ошибки, описанные в статье (кроме пунктов 14–15), были обнаружены в реальной базе при выполнении достаточно сложных запросов, однако в статье приведены только их сокращённые части. Поэтому отсутствие ошибки на упрощённом тестовом примере не гарантирует, что ошибка в новом релизе платформы исправлена — ошибка может проявиться в реальном более сложном запросе. Таким образом, рекомендую всегда тщательно тестировать окончательные варианты своих запросов, особенно если они похожи на запросы, приведённые в пунктах 3–4, 6–10, 14–15.
1. Особенность обработки сложных логических выражений
Актуальность. Данная особенность должна быть во всех релизах 1С:Предприятие 7.7.
Все составные части сложного логического выражения вычисляются полностью, даже когда его результат уже не может измениться. Например:
Если (Док.Вид()="ПриходнаяНакладная")
И (Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя) Тогда
В случае если документ Док имеет другой вид, второе условие всё равно будет проверяться и будет выдана ошибка программы для документов, у которых нет реквизита ПризнакНакладной.
Обход проблемы.
Проверять условия поочерёдно:
Если Док.Вид()="ПриходнаяНакладная" Тогда
Если Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя Тогда
Примечание.
Это широкоизвестные «грабли», однако известны они, как правило, только тем широким кругам программистов 1С, которые на них уже наступали самостоятельно. В 8-й версии эти «грабли», наконец, убрали. Вычисление только необходимых частей логического выражения, кроме повышения быстродействия, позволяет создавать более короткий и более читаемый код. Например, можно было бы написать:
Если ((Док.Вид()="ПриходнаяНакладная") И (Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя))
ИЛИ ((Док.Вид()="РасходИзКошелька") И (Док.ВидОплаты=Перечисление.ВидыОплаты.Возврат))
Тогда
...
КонецЕсли;
Однако в 1С 7 приходится в таком случае создавать более громоздкую и менее наглядную конструкцию:
Обрабатывать=0;
Если Док.Вид()="ПриходнаяНакладная" Тогда
Если Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя Тогда
Обрабатывать=1;
КонецЕсли;
ИначеЕсли Док.Вид()="РасходИзКошелька" Тогда
Если Док.ВидОплаты=Перечисление.ВидыОплаты.Возврат Тогда
Обрабатывать=1;
КонецЕсли;
КонецЕсли;
Если Обрабатывать=1 Тогда
...
КонецЕсли;
2. Ошибка получения наименования элемента справочника с ведущими пробелами
Актуальность. Данная ошибка была обнаружена в релизе 17 в базе формата DBF. В релизе 27 она по-прежнему присутствует и в базе формата DBF, и в базе формата SQL.
При вызовах в программных модулях реквизита Наименование элемента справочника он передаётся с усечёнными пробелами в начале и конце. Например, если длина наименования установлена 10 символов и для какого-то элемента введено наименование " 123", то при команде:
Стр=Спр.Наименование;
получим, что Стр="123", а не Стр=" 123 " и не Стр=" 123".
Здесь неприятно, что теряем возможные пробелы в начале. И если усечение пробелов в конце можно считать лишь особенностью языка, то в начале — ошибкой.
Примечание.
Усечение пробелов в начале других строковых реквизитов не происходит.
Обход ошибки.
В общем случае в объектной модели мною способ решения этой проблемы не найден, так как не получается получить информацию о наличии ведущих пробелов в наименовании. Однако при выполнении запроса типа:
Рекв=Справочник.МойСправочник.Наименование
никаких усечений пробелов не произойдёт (ни в начале, ни в конце) и из атрибута Рекв запроса можно будет получить правильное наименование.
Для частных случаев можно также учесть ожидаемую форму наименования. Например, если есть элементы справочника с наименованиями вида:
1. Первый
2. Второй
...
9. Девятый
10. Десятый
Пробел впереди первых девяти наименований позволяет обеспечить правильную сортировку. И в форме списка справочника это выполняется. Однако если мы хотим получить список значений:
Список=СоздатьОбъект("СписокЗначений");
Спр=СоздатьОбъект("Справочник.Номенклатура");
Спр.ВыбратьЭлементы();
Пока Спр.ПолучитьЭлемент()=1 Цикл
Список.ДобавитьЗначение(Спр.ТекущийЭлемент(),Спр.Наименование);
КонецЦикла;
тогда ведущие пробелы будут в представлении элементов списка утеряны.
Чтобы их сохранить, внутри цикла можно сделать так:
НазваниеЭлемента=?(Сред(Спр.Наименование,2,1)="."," ","")+Спр.Наименование;
Список.ДобавитьЗначение(Спр.ТекущийЭлемент(),НазваниеЭлемента);
3. Ошибка запроса по месяцам
Актуальность. Данная ошибка была обнаружена в релизе 17 в базе формата DBF. Для других релизов и формата базы SQL проверок не производилось.
Когда в запросе указана группировка по месяцам и два регистра, например:
Клиент = Регистр.ВзаиморасчетыПокупателей.Клиент,
Регистр.ВзаиморасчетыПоставщиков.Клиент;
ДолгПокуп = Регистр.ВзаиморасчетыПокупателей.Долг;
ДолгПост = Регистр.ВзаиморасчетыПоставщиков.Долг;
Группировка Клиент Без групп;
Группировка Месяц ВСЕ;
Функция НачДолг = НачОст(ДолгПокуп);
Функция КонДолг = КонОст(ДолгПокуп);
Функция РасхДолг = Расход(ДолгПокуп);
Функция ПрихДолг = Приход(ДолгПокуп);
Функция НачДолгПост = НачОст(ДолгПост);
Функция КонДолгПост = КонОст(ДолгПост);
Функция РасхДолгПост = Расход(ДолгПост);
Функция ПрихДолгПост = Приход(ДолгПост);
или справочник и регистр, например:
Товар = Справочник.Номенклатура.ТекущийЭлемент,
Регистр.ПартииТоваров.Товар;
тогда функция начального остатка одного из регистров/регистра выдаёт всегда нулевой результат.
Обход ошибки.
Из конечного остатка вычитать приход и добавлять расход.
4. Ошибка запроса со справочником, регистром и условием вхождения в список значений
Актуальность. Данная ошибка была обнаружена в релизе 17 в базе формата DBF. Для других релизов и формата базы SQL проверок не производилось.
Когда в запросе указан справочник и регистр, а также условие вхождения в список значений, например:
Клиент = Справочник.Контрагенты.ТекущийЭлемент,
Регистр.ВзаиморасчетыПокупателей.Клиент;
ДолгПокуп=Регистр.ВзаиморасчетыПокупателей.Долг;
Группировка Клиент Без групп;
Функция НачДолг = НачОст(ДолгПокуп);
Функция КонДолг = КонОст(ДолгПокуп);
Функция РасхДолг = Расход(ДолгПокуп);
Функция ПрихДолг = Приход(ДолгПокуп);
Условие (Клиент в ВыбКлиент);
то в случае, если ВыбКлиент — это список значений с одним элементом, не являющимся группой, правильно рассчитывается только функция начального остатка, расход и приход же всегда получаются нулевыми, а конечный остаток всегда совпадает с начальным. Если же ВыбКлиент — это список значений из нескольких элементов или одного элемента, но являющегося группой (даже состоящей из одного элемента справочника), то всё работает нормально.
Обход ошибки.
Анализировать предварительно список значений и, когда в нём только один элемент, не являющийся группой, то не указывать в запросе справочник, а только регистр.
5. Ошибка метода Доступность в форме списка справочника
Актуальность. Данная ошибка была обнаружена в релизе 17. В релизе 27 она по-прежнему присутствует.
В форме списка справочника метод Доступность не работает для реквизитов табличной формы. Если в форме списка разрешено редактирование, то для отдельных реквизитов его нельзя запретить ни установкой флажка «Сделать недоступным» в свойствах поля ввода в Конфигураторе, ни программно, например:
Форма.КатегорияПрав.Доступность(0);
Обход ошибки.
Запретить редактирование непосредственно в поле ввода для реквизитов типа Число, Строка, Дата, Счет можно с помощью метода Редактирование, который работает как программно, так и установкой флажка «Запретить редактирование». Однако этот метод не отключает кнопку выбора, поэтому он поможет только в некоторых случаях. Если потребность запретить правку отдельных реквизитов связана, например, с правами пользователя, то можно воспользоваться методом Видимость, который тоже работает нормально. Если всё же необходимо показать реквизиты без возможности редактирования, то можно создать текстовые колонки, которым присваивать нужные значения программно.
6. Ошибка запроса с несколькими условиями для одного параметра
Актуальность. Данная ошибка была обнаружена в релизе 25 в базе формата SQL. Для других релизов и формата базы DBF проверок не производилось.
Когда в запросе указано несколько условий с неравенством, относящихся к одному параметру, и условие с равенствами и логическим оператором ИЛИ, например:
Статус=Регистр.ПартииТоваров.Статус;
Условие (Статус<>УчетОказанныхУслуг);
Условие (Статус<>Купленный);
Условие ((Статус=ОтданныйКупленный) ИЛИ (Статус=ОтданныйПринятый));
то последнее условие с оператором ИЛИ игнорируется для ряда функций (но не для всех!).
Примечание.
Такое, на первый взгляд, странное сочетание условий может получиться при программном формировании текста запроса по различным фильтрам в сложном отчёте.
Обход ошибки.
Использовать либо только полный набор условий с неравенствами, либо только одно условие для данного параметра с равенствами, соединёнными между собой логическим оператором ИЛИ.
Примечание.
Второй вариант позволяет избежать ошибки, описанной в данном пункте, но он может привести к ошибке, описанной в следующем пункте.
7. Ошибка запроса с условием с логическим оператором «ИЛИ» или «И»
Актуальность. Данная ошибка была обнаружена в релизе 25 в базе формата SQL. Для других релизов и формата базы DBF проверок не производилось.
Когда в запросе указано условие с проверками на равенство значений и логическим оператором ИЛИ, например:
Статус=Регистр.ПартииТоваров.Статус;
Условие ((Статус=ОтданныйКупленный) ИЛИ (Статус=ОтданныйПринятый));
или с проверками на неравенство значений и логическим оператором И, например:
Условие ((Статус<>УчетОказанныхУслуг) И (Статус<>Купленный) И (Статус<>Принятый));
то данное условие игнорируется для функции КонОст при конечной дате, отличной от даты точки актуальности и последних дат месяцев (то есть в данном примере запрос может произойти по всем статусам).
Примечание.
Такую ошибку я получал не во всех запросах с аналогичными условиями. Возможно, эта ошибка возникает только для строчных переменных длины 1 (в примере Статус — это измерение со значением, состоящим из одного символа, а ОтданныйКупленный, ОтданныйПринятый, УчетОказанныхУслуг, Купленный, Принятый — это переменные, которые имеют значение, состоящее из одного символа).
Обход ошибки.
Использовать условие с проверкой на вхождение в список значений:
СписокСтатусов=СоздатьОбъект("СписокЗначений");
СписокСтатусов.ДобавитьЗначение(ОтданныйКупленный);
СписокСтатусов.ДобавитьЗначение(ОтданныйПринятый);
...
Условие (Статус в СписокСтатусов);
или набор условий неравенств на отдельных строках:
Условие (Статус<>УчетОказанныхУслуг);
Условие (Статус<>Купленный);
Условие (Статус<>Принятый);
8. Ошибка запроса по нескольким регистрам с ресурсами разной точности
Актуальность. Данная ошибка была обнаружена в релизе 25 в базе формата SQL. Для других релизов и формата базы DBF проверок не производилось.
Когда в запросе переменной присваивается значение ресурсов нескольких регистров, например:
Долг=Регистр.ВзаиморасчетыПокупателей.Долг,
Регистр.ВзаиморасчетыПоставщиков.Долг,
Регистр.Производство.Стоимость;
Функция НачДолг=НачОст(Долг);
Функция КонДолг=КонОст(Долг);
и точность ресурсов различна, например:
ВзаиморасчетыПокупателей.Долг - Число 15.5;
ВзаиморасчетыПоставщиков.Долг - Число 15.5;
Производство.Стоимость - Число 19.2;
то функции НачОст и КонОст дают немного неверный результат — такое впечатление, что все движения регистров округляются или усекаются до меньшей точности (в примере до сотых числа).
Обход ошибки.
Оказалось, достаточно поменять порядок регистров в запросе:
Долг=Регистр.Производство.Стоимость,
Регистр.ВзаиморасчетыПокупателей.Долг,
Регистр.ВзаиморасчетыПоставщиков.Долг;
Примечание.
Как показывает практика, правило «От перемены мест слагаемых сумма не меняется», которому мы верили с первого класса, работает не всегда. По крайней мере, в запросах 1С 7.7. У меня был также случай, когда вроде бы корректный запрос 1С приводил к ошибке SQL-сервера, и этой ошибки удалось избежать, просто поменяв две строки в запросе местами. К сожалению, эту проблему я в свой список ошибок 1С своевременно не занёс, а потом уже точно не мог вспомнить, какой был запрос.
9. Ошибка запроса с отрицанием вхождения в список в условии Когда для функции
Актуальность. Данная ошибка была обнаружена в релизе 25 в базе формата SQL. Для других релизов и формата базы DBF проверок не производилось.
Когда в запросе для функции указано условие Когда с отрицанием вхождения в список, например:
КодОперации = Регистр.ВзаиморасчетыПокупателей.КодОперации;
ДолгПокуп = Регистр.ВзаиморасчетыПокупателей.Долг;
Функция ПрихДолг = Приход(ДолгПокуп) Когда (НЕ(КодОперации в СписокКодовОпераций));
то функция для пустого значения КодОперации не выполняется, хоть пустое значение и не входит в список СписокКодовОпераций.
Примечание.
При условии без отрицания:
Функция ПрихДолг = Приход(ДолгПокуп) Когда (КодОперации в СписокКодовОпераций);
функция для пустого значения КодОперации тоже не выполняется, если пустое значение данного типа не входит в список СписокКодовОпераций, однако это уже правильно.
Обход ошибки.
В условии Когда дополнительно явным образом проверять на равенство пустому значению:
Функция ПрихДолг = Приход(ДолгПокуп) Когда ((НЕ(КодОперации в СписокКодовОпераций))
ИЛИ (ПустоеЗначение(КодОперации)=1));
Примечание.
Для тех, кто думает, что пустое значение всегда принадлежит любому множеству, особо отмечу, что это не верно. В математике правильно так: пустое множество является подмножеством любого множества, а пустое множество — это множество, не содержащее ни одного элемента. Таким образом, элемент со значением 0 не является пустым множеством и не принадлежит множеству {1,2,3}. В 1С 8 аналогом пустого множества является NULL, в 1С 7 такого аналога в языке нет. Легко можно проверить:
Б=СоздатьОбъект("СписокЗначений");
Для Инд=1 По 5 Цикл
Если Инд=1 Тогда
Б.ДобавитьЗначение(1);
Б.ДобавитьЗначение(2);
Б.ДобавитьЗначение(3);
ИначеЕсли Инд=2 Тогда
Б.ДобавитьЗначение(0);
ИначеЕсли Инд=3 Тогда
Б.ДобавитьЗначение(" ");
ИначеЕсли Инд=4 Тогда
Б.ДобавитьЗначение(" ");
ИначеЕсли Инд=5 Тогда
Б.ДобавитьЗначение("");
КонецЕсли;
Сообщить("Список значений {"+Б.ВСтрокуСРазделителями()+"}");
Т1="";
Т2="";
Для Инд2=1 По 5 Цикл
Если Инд2=1 Тогда
А=0;
ИначеЕсли Инд2=2 Тогда
А=" ";
ИначеЕсли Инд2=3 Тогда
А=" ";
ИначеЕсли Инд2=4 Тогда
А="";
ИначеЕсли Инд2=5 Тогда
А=ПолучитьПустоеЗначение();
КонецЕсли;
Если ТипЗначенияСтр(А)="Строка" Тогда
ПроверяемоеЗначение = " {""" + А + """}";
Иначе
ПроверяемоеЗначение = " {" + А + "}";
КонецЕсли;
Если Б.Принадлежит(А)=1 Тогда
Т1 = Т1 + ?(Т1="","","; ") + ПроверяемоеЗначение;
Иначе
Т2 = Т2 + ?(Т2="","","; ") + ПроверяемоеЗначение;
КонецЕсли;
КонецЦикла;
Сообщить(" Принадлежит списку: " + Т1);
Сообщить(" НЕ принадлежит списку: " + Т2);
КонецЦикла;
Получаем:
Список значений {1,2,3}
Принадлежит списку:
НЕ принадлежит списку: {0}; {" "}; {" "}; {""}; {}
Список значений {1,2,3,0}
Принадлежит списку: {0}
НЕ принадлежит списку: {" "}; {" "}; {""}; {}
Список значений {1,2,3,0," "}
Принадлежит списку: {0}; {" "}
НЕ принадлежит списку: {" "}; {""}; {}
Список значений {1,2,3,0," "," "}
Принадлежит списку: {0}; {" "}; {" "}
НЕ принадлежит списку: {""}; {}
Список значений {1,2,3,0," "," ",""}
Принадлежит списку: {0}; {" "}; {" "}; {""}
НЕ принадлежит списку: {}
Также можно проверить работу логического оператора принадлежности запроса:
Рекв=Справочник.МойСправочник.МойРеквизит;
Условие (Рекв в Б);
Если кто-то захочет проверить в условии запроса вместо внутренней переменной Рекв внешнюю переменную А, тогда см. пункт 14. Также см. пункт 15.
10. Ошибка запроса с отрицанием вхождения в список в условии
Актуальность. Данная ошибка была обнаружена в релизе 25 в базе формата SQL. Для других релизов и формата базы DBF проверок не производилось.
Когда в запросе указано условие с отрицанием вхождения в список, например:
ВидОтгрузки = Регистр.ВзаиморасчетыПокупателей.ВидОтгрузки;
Условие (НЕ(ВидОтгрузки в СписокВидовОтгрузки));
и в списке СписокВидовОтгрузки нет пустого значения, а ВидОтгрузки в периоде запроса для некоторых записей в регистре имеет пустое значение, но для всех остальных записей регистра входит в список СписокВидовОтгрузки, тогда функции НачОст и КонОст срабатывают правильно и показывают результат только для пустых ВидОтгрузки, однако функции Приход и Расход работают неверно и показывают результат для всех ВидОтгрузки.
Обход ошибки.
В условии дополнительно явным образом проверять на равенство пустому значению:
Условие ((НЕ(ВидОтгрузки в СписокВидовОтгрузки))
ИЛИ (ПустоеЗначение(ВидОтгрузки)=1));
Примечание.
См. примечание к предыдущему пункту.
11. Ошибка получения периодического реквизита пустого значения справочника
Актуальность. Данная ошибка была обнаружена в релизе 25 в базе формата SQL. Для других релизов и формата базы DBF проверок не производилось.
Когда берём пустое значение справочника, то его периодический реквизит может оказаться непустым, например:
ЦенаТовара=ПолучитьПустоеЗначение("Справочник.Цены");
ДатаЦены='01.01.2009';
ЦенаЦены=ЦенаТовара.Цена.Получить(ДатаЦены);
В этом случае ЦенаЦены может оказаться почему-то вовсе не 0.
Обход ошибки.
Надо проверять элемент справочника на пустое значение и явным образом назначать переменным пустые значения, а не брать их всегда с периодических реквизитов:
Если ПустоеЗначение(ЦенаТовара)=1 Тогда
ЦенаЦены=0;
Иначе
ЦенаЦены=ЦенаТовара.Цена.Получить(ДатаЦены);
КонецЕсли;
12. Особенность сортировки таблицы значений с элементами справочника
Актуальность. Данная особенность должна быть во всех релизах 1С:Предприятие 7.7.
Таблица значений сортируется по колонке с элементами справочника по их основному представлению. Если основное представление в виде наименования и в таблице есть элементы справочника с одинаковыми наименованиями, то при сортировке они могут оказаться между собой в любом порядке, в том числе вперемешку строки, относящиеся к разным элементам справочника с одинаковыми наименованиями (рассматриваем случай, когда для каждого элемента справочника в таблице может быть несколько строк). Например:
Табл.Сортировать("Клиент");
Обход проблемы.
Если надо отсортировать таблицу значений по наименованию элементов справочника, но при этом избежать перемешивания строк с элементами, имеющими одинаковое наименование, тогда в таблице можно сформировать колонку с кодом или другим уникальным реквизитом элемента справочника и сортировать таблицу также по этой колонке. Например:
Табл.Сортировать("Клиент,КлиентКод");
Примечание.
Как сообщили BlueWind и TesterMe, более простой и универсальный способ с помощью сортировки по внутреннему значению
Табл.Сортировать("Клиент+,Клиент*");
почему-то работает по принципу «как звёзды встанут».
13. Особенности сортировки таблицы значений, списка значений, справочника и запроса
Актуальность. Данные сортировки первоначально мною тестировались в релизе 25 в базах формата и SQL, и DBF. Проверка в релизе 27 в базах обоих форматов подтвердила, что ничего не изменилось.
Как удалось выяснить экспериментальным путём, 1С:Предприятие использует для сортировок два варианта. Первый вариант — это порядок, заданный в кодовой таблице, которая была выбрана при создании базы (обычно это CP-1251). При втором варианте используются следующие правила:
1) Символы «апостроф», «дефис» («минус»), «мягкий перенос», «короткое тире», «длинное тире» при сортировке вообще игнорируются.
2) Игнорируется регистр всех букв при сортировке.
3) Почти все символы знаков идут раньше цифр, затем цифры, латинские буквы, буквы кириллицы. При этом символ «№» располагается после латинской буквы «N», символ «™» после латинской буквы «T», буква «Ё» после русской буквы «Е», буквы кириллицы не русского алфавита после соответствующих им букв русского алфавита.
Таблица значений сортируется по колонке со строковыми данными по первому варианту, а список значений со строковыми данными сортируется по второму варианту. Например:
Список=СоздатьОбъект("СписокЗначений");
Табл=СоздатьОбъект("ТаблицаЗначений");
Табл.НоваяКолонка("Кол1");
Для Инд=1 По 7 Цикл
Если Инд=1 Тогда
Стр="Автоагрегат";
ИначеЕсли Инд=2 Тогда
Стр="Авто-Аякс";
ИначеЕсли Инд=3 Тогда
Стр="Авида";
ИначеЕсли Инд=4 Тогда
Стр="АВТО МОТОР";
ИначеЕсли Инд=5 Тогда
Стр="№26";
ИначеЕсли Инд=6 Тогда
Стр="SAS";
ИначеЕсли Инд=7 Тогда
Стр="NIGAR";
КонецЕсли;
Список.ДобавитьЗначение(Стр);
Табл.НоваяСтрока();
Табл.Кол1=Стр;
КонецЦикла;
Сообщить("Таблица значений:");
Табл.Сортировать("Кол1");
Табл.ВыбратьСтроки();
Пока Табл.ПолучитьСтроку()=1 Цикл
Сообщить("*"+Табл.Кол1+"*");
КонецЦикла;
Список.Сортировать();
Сообщить("Список значений:");
Для НомСтр=1 По Список.РазмерСписка() Цикл
Сообщить("*"+Список.ПолучитьЗначение(НомСтр)+"*");
КонецЦикла;
Получаем:
Таблица значений:
*NIGAR*
*SAS*
*№26*
*АВТО МОТОР*
*Авида*
*Авто-Аякс*
*Автоагрегат*
Список значений:
*NIGAR*
*№26*
*SAS*
*Авида*
*АВТО МОТОР*
*Автоагрегат*
*Авто-Аякс*
Однако если данными являются элементы справочника, то сортировка происходит наоборот: таблица значений сортирует по второму варианту, а список значений — по первому.
Если же сортировать список значений по представлению (метод СортироватьПоПредставлению), то даже, если представление явным образом в список не заносилось, тогда сортировка и для строковых данных и для элементов справочника будет идти по второму варианту сортировки.
В запросах упорядочивание по наименованию элементов справочника идёт по второму варианту, но без использования первого правила (апостроф и «чёрточки» учитываются, поэтому, например, Авто-Аякс окажется перед Автоагрегат).
В диалоговых формах списков справочников сортировка по наименованию идёт по второму варианту, причём при формате базы DBF без использования первого правила, а при формате базы SQL первое правило используется (что весьма странно, так как в запросах первое правило не используется).
Примечание.
Как говорилось в старом анекдоте: «Это невозможно понять — это надо просто запомнить».
14. Ошибки запроса с внешней переменной в качестве проверяемого значения в условии с логическим оператором принадлежности
Актуальность. Данные ошибки были обнаружены в релизе 27. Проверялось и в базе формата DBF, и в базе формата SQL.
Когда в условиях запросов с логическим оператором принадлежности проверяется принадлежность внешней переменной внешнему списку значений:
Условие (А в Б);
Условие (НЕ(А в Б));
тогда в базе любого формата при любых непустых значениях А типа Число, Строка, Дата всегда считается (и для запроса с первым условием, и для запроса со вторым условием), что А не является подмножеством Б, т.е. вычисление логического выражения первого условия всегда даёт значение «ложь», а второго — «истина», даже, например, для случаев:
А=2 и Б={1,2,3}
А="2" и Б={"1","2","3"}
А='02.01.2009' и Б={'01.01.2009','02.01.2009'}
При пустом числовом значении (А=0) и пустом строковом ненулевой длины (А=" ", А=" " и т.д.) для любого списка Б (в т.ч. содержащего пустые числовые и пустые строковые значения) в базе формата DBF также первое условие всегда ложно, а второе истинно. Однако в базе формата SQL в этом случае всё наоборот — эти пустые значения всегда считаются подмножеством любого списка.
А при следующих пустых значениях:
А=ПолучитьПустоеЗначение()
А=ПолучитьПустоеЗначение("Строка") (А="")
А=ПолучитьПустоеЗначение("Дата") (А='')
А=ПолучитьПустоеЗначение("Справочник.МойСправочник")
А=ПолучитьПустоеЗначение("Документ.МойДокумент")
в базе любого формата возникает неопределённость: оба условия всегда истинны вне зависимости от содержимого списка Б, т.е. с одной стороны эти пустые значения считаются подмножеством любого списка, а с другой стороны — не считаются.
Кроме того, в базах обоих форматов запросы с подобными условиями для непустых значений типа Документ ведут себя также некорректно, как и для базовых типов. Однако запросы для непустых значений типа Справочник ведут себя вполне корректно.
Вот такая неразбериха…
Обход ошибки.
В условии внешние переменные, кроме типа Справочник, проверять только на равенство, а не на принадлежность:
Условие (А = Б);
Условие (НЕ(А = Б));
Условие (А <> Б);
При таких условиях запрос работает корректно (Б здесь переменная, а не список значений). Однако и тут не всё гладко: в базе формата DBF учитывается различие типа значений переменных, в том числе длины для строковых (т.е. считается, что ПолучитьПустоеЗначение()<>"", ""<>" " и " "<>" "), а в базе формате SQL перед сравнением переменные приводятся к одному типу (т.е. считается, что все пустые переменные базовых типов равны, а также, например, "2"=2), но попытка привести к одному типу число и значение типа Справочник заканчивается аварийным завершением программы.
15. Ошибка запроса с внутренней переменной в качестве проверяемого значения в условии с логическим оператором принадлежности и отрицанием
Актуальность. Данная ошибка была обнаружена в релизе 27 в базе формата SQL. В релизе 27 в базе формата DBF этой ошибки нет.
Когда в запросе с условием, где проверяется не вхождение внутренней переменной во внешний список значений:
Рекв=Справочник.МойСправочник.МойРеквизит;
Условие (НЕ(Рекв в Б));
реквизит МойРеквизит имеет базовый тип (Число, Строка, Дата) и список Б содержит больше одного элемента, тогда условие всегда получается истинным.
Примечание.
Такие же запросы без отрицания выполняются корректно, в том числе для пустых значений.
Обход ошибки.
В условии проверять на неравенства вместо отрицания принадлежности.