gifts2017

Некоторые ошибки и «проблемные» особенности реализации встроенного языка платформы 1С:Предприятие 7.7

Опубликовал Александр Рыжов (AleksR) в раздел Программирование - Практика программирования

Описание некоторых ошибок и особенностей реализации встроенного языка, которые создают «подводные камни» при программировании, а также возможных способов обхода этих проблем из собственного опыта работы на платформе 1С:Предприятие 7.7.

Настоящая статья не претендует на полноту описания и подробный анализ ошибок и «подводных камней» встроенного языка платформы 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 этой ошибки нет.

Когда в запросе с условием, где проверяется не вхождение внутренней переменной во внешний список значений:

Рекв=Справочник.МойСправочник.МойРеквизит;
Условие (НЕ(Рекв в Б));

реквизит МойРеквизит имеет базовый тип (Число, Строка, Дата) и список Б содержит больше одного элемента, тогда условие всегда получается истинным.

Примечание.

Такие же запросы без отрицания выполняются корректно, в том числе для пустых значений.

Обход ошибки.

В условии проверять на неравенства вместо отрицания принадлежности.

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Дмитрий Литовченко (kompas-dm) 08.04.11 13:15
(0) Просто, грамотным языком и ненавязчиво. Только это 0,01%. Остальное - опыт, практика, тестирование
2. г. Казань Рустем Гумеров (Rustig) 08.04.11 14:22
Спасибо за статью! Так держать! :)
3. Александр Рытов (Арчибальд) 08.04.11 14:29
(1) Насчет ненавязчиво я бы поспорил.
Первый пример вообще ни к селу ни к городу.
Все составные части сложного логического выражения вычисляются полностью, даже когда его результат уже не может измениться.
Автор по-видимому считает, что при логическом умножении логического значения "Ложь" на что угодно, хоть на стеариновую свечку, должна таки ложь получиться.
Девятый и десятый примеры. Всякому известно, что пустое множество является подмножеством любого множества. А в ЖКК условие вхождения описывается именно в терминах множеств.
Двенадцатый пример - вообще ни о чем. Понятно же, что сортировка по значению элемента справочника бессмысленна, ибо неповторяема. "Сортировка" будет такой же, как в восьмерочной выборке ДЛЯ КАЖДОГО...
Тринадцатый пример основан на неявном предположении автора о том, что строка и представление строки - это одно и то же. Если из этого неверного предположения не исходить (а с какой стати?), то текст опять-таки оказывается ни о чем.
4. Дмитрий Литовченко (kompas-dm) 08.04.11 14:43
(3) Арчибальд не дремлет...
С замечаниями согласен. Тема хорошая и нужная для начинающих 1С-ников.
5. Александр Рытов (Арчибальд) 08.04.11 14:46
(4) Заметь, минуса я не поставил, хотя первый пункт меня сильно напряг. :D Все же пользы от статьи больше, чем вреда.
6. Александр Рыжов (AleksR) 08.04.11 15:22
(3)
Автор по-видимому считает, что при логическом умножении логического значения "Ложь" на что угодно, хоть на стеариновую свечку, должна таки ложь получиться.

Программист, когда пишет логическое выражение, ожидает, что его результатом будет либо "Истина", либо "Ложь", а вовсе не "стеариновая свечка" в виде ошибки программы. И он ожидает, что если первый сомножитель "Ложь", то дальше уже проверять незачем. И в 8-й версии 1С:Предприятие это поведение исправлено.

Девятый и десятый примеры. Всякому известно, что пустое множество является подмножеством любого множества. А в ЖКК условие вхождения описывается именно в терминах множеств.

Если Вы внимательнее прочтёте 10-й пункт, то увидете, что ситуация неоднозначна (ошибочна): 1С то считает, что пустое значение входит в список, и одновременно (для каких-то условий и функций) не считает.

Двенадцатый пример - вообще ни о чем. Понятно же, что сортировка по значению элемента справочника бессмысленна, ибо
неповторяема. "Сортировка" будет такой же, как в восьмерочной выборке ДЛЯ КАЖДОГО...

В 1С 7.7 сортировка по значению элемента справочника вовсе не бессмысленна. Как я написал в статье: "Таблица значений сортируется по колонке с элементами справочника по их основному представлению".

Тринадцатый пример основан на неявном предположении автора о том, что строка и представление строки - это одно и то же. Если из этого неверного предположения не исходить (а с какой стати?), то текст опять-таки оказывается ни о чем.

Тринадцатый пункт о том, какую именно сортировку мы получим в различных случаях. И если Вы считаете, что всё это очевидно, то я удивлён.
7. Александр Рытов (Арчибальд) 08.04.11 16:08
(6)
И в 8-й версии 1С:Предприятие это поведение исправлено
Т.е. в восьмерке правильным будет написать
Если Истина ИЛИ 8 Тогда
Программист, когда пишет логическое выражение, ожидает, что его результатом будет либо "Истина", либо "Ложь"
Программист не должен ожидать. Он должен обеспечить, чтобы операнды всех логических операций были логическими. Если он не обеспечил - это его ошибка.
Если Вы внимательнее прочтёте 10-й пункт, то увидете, что ситуация неоднозначна
Ситуация как раз однозначна: некорректное написание условия породило некорректную его обработку.
Сортировка по значению справочника, естественно, происходит по основному представлению. Но если я пишу универсальную обработку, я не знаю каково будет это основное представление в момент запуска, т.е. я не имею права делать об этом какие-либо предположения. Возьму назад только термин "бессмысленна". Вместо него следует читать "некорректна/ошибочна".
Тринадцатый пункт не дает ответа на вопрос, как именно сортируются объекты-строки. Кое-какие частичные наблюдения. Поскольку полного представления об этом нет, результат сортировки непредсказуем, и значит, хороший программист не сможет ей воспользоваться.
8. Артур Аюханов (artbear) 08.04.11 16:23
Первый пункт четко и точно описан в ЖКК, значит, это не баг, а фича.
Это известная фича 1С 77.
9. Александр Рыжов (AleksR) 08.04.11 16:58
(7)
Программист не должен ожидать. Он должен обеспечить, чтобы операнды всех логических операций были логическими. Если он не обеспечил - это его ошибка.

В приведённом в статье примере оба операнда логические:

Если (Док.Вид()="ПриходнаяНакладная")
И (Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя) Тогда

Но при этом в 1С 7.7 мы можем наступить на "грабли", если документ Док не имеет реквизита ПризнакНакладной, а в 1С 8 этой проблемы уже нет. И я не пишу, что это ошибка 1С, а пишу, что это особенность обработки сложных логических выражений. Мне непонятно, что Вам не нравится.

Но если я пишу универсальную обработку, я не знаю каково будет это основное представление в момент запуска, т.е. я не имею права делать об этом какие-либо предположения.

А я разве предлагаю всегда сортировать по самому элементу справочника? Я пишу про особенность сортировки таблицы значений, когда сортируем по колонке с элементом справочника. В большинстве случаев представление нам известно. Если мы отсортируем явным образом по наименованию, то можем получить неприятность при одинаковых наименованиях. И это достаточно очевидно. А речь в статье как раз идёт о том, что отсортировав по самому элементу справочника, мы можем получить ту же неприятность, что несколько менее очевидно.

Ситуация как раз однозначна: некорректное написание условия породило некорректную его обработку

В чём некорректность этого условия?
Условие (НЕ(ВидОтгрузки в СписокВидовОтгрузки));

Тринадцатый пункт не дает ответа на вопрос, как именно сортируются объекты-строки. Кое-какие частичные наблюдения.

А я в самом начале написал: "Настоящая статья не претендует на полноту описания и подробный анализ ошибок и «подводных камней» встроенного языка платформы 1С:Предприятие 7.7". Я не теоретик, я описал реальные случаи, с которыми пришлось столкнуться на практике. И это описание (в сокращённом виде, конечно) использовал раньше сам для себя, чтобы не наступать на одни и те же "грабли". И надеюсь, кому-то это тоже пригодится. А в тринадцатом пункте как раз описаны все самые распространённые случаи.
10. Александр Рытов (Арчибальд) 08.04.11 17:21
(9) (Док.ПризнакНакладной = Перечисление.ПризнПрихНакл.ВозвратОтПокупателя) - это операнд неопределенного типа, поскольку сравнивать можно лишь однотипные значения, а значение слева вовсе не обязано иметь тип перечисления, что справа. И если в восьмерке такой финт проходит, то это ошибка восьмерки. Выражение "(1=1) ИЛИ 8" не должно проходить синтаксического контроля. В очередном релизе платформы фирма 1С имеет полное право ошибку исправить...

ИМХО, большая часть описанного проистекает не из "ошибок и особенностей реализации", а из неряшливости программирования.
11. Александр Рыжов (AleksR) 08.04.11 19:39
(10)
Выражение "(1=1) ИЛИ 8" не должно проходить синтаксического контроля. В очередном релизе платформы фирма 1С имеет полное право ошибку исправить...

Это Ваше выражение, а не моё. А вот выражение "(1=1) ИЛИ МояПеременная" всегда будет проходить синтаксический контроль и всегда будет давать "Истина" в 1С 8, в отличие от 1С 7, вне зависимости от значения переменной МояПеременная.

(3)
Всякому известно, что пустое множество является подмножеством любого множества. А в ЖКК условие вхождения описывается именно в терминах множеств.

А вот программисты 1С 7.7 не пришли в этом вопросе к полному консенсусу (по крайней мере в релизе 25).

Функция ПрихДолг = Приход(ДолгПокуп) Когда (НЕ(КодОперации в СписокКодовОпераций));

Функция ПрихДолг = Приход(ДолгПокуп) Когда (КодОперации в СписокКодовОпераций);

Если пустое значение не входит в список СписокКодовОпераций, то функция для пустого значения КодОперации не выполняется ни в первом, ни во втором случае.
12. Вячеслав Кадацкий (marsohod) 09.04.11 01:13
(10)(11) Господа, Вы спорите по одной простой причине: в языке 1С нет операторов && и ||. Разработчики других языков (например того же PERL) не считают себя самыми умными.

Бинарное && - логическое И. Если левый аргумент FALSE, то правый не проверяется.
Бинарное || - логическое ИЛИ. Если левый аргумент TRUE, то правый аргумент не проверяется.
13. Василий Казьмин (awk) 09.04.11 13:15
(0) Маленькая справка по первому пункту:
В 1С 8 не возникнет ошибки не потому, что второй операнд проигнарируется, а потому, что будут сравнивается на равенство типы. Более подробно об этом можно почитать в синтекс-помошнике.
14. Александр Рыжов (AleksR) 10.04.11 14:01
(13) Цитата из документации "1С:Предприятие 8.1. Конфигурирование и администрирование", страница 1-251:
===========================
Замечание. При вычислении логического выражения вычисляются только необходимые части выражения. Например, в выражении (Цена > 0) И ПроверкаСуммы(), если Цена <= 0, то функция ПроверкаСуммы() не вызывается.
===========================
15. Sergey Sergey (Sergant1) 11.04.11 16:38
AleksR хорошая полезная статья.
Арчибальд критика была не по существу.
16. Александр Рытов (Арчибальд) 11.04.11 17:14
(14)
При вычислении логического выражения вычисляются только необходимые части выражения. Например, в выражении (Цена > 0) И ПроверкаСуммы(), если Цена <= 0, то функция ПроверкаСуммы() не вызывается.
... даже и в том случае, когда функция ПроверкаСуммы() имеет результат не логического типа или вообще дает ошибку при вычислении. Неряшливый программист - семерочник из первого примера эту ошибку увидит сразу. Неряшливый программист - восьмерочник может долго еще думать, что он все правильно написал. Грамотный программист озаботится тем, чтобы оба операнда логического выражения были логическими.
Dolly_EV; vkr; +2 Ответить 1
17. vkr (vkr) 12.04.11 09:23
(16) Не стоило бы, наверное, так строго судить... Автору - спасибо за заботу, 1С-ке - минус за глюки! :)
Вы, наверное, тоже могли бы поделиться чем-то аналогичным из своего огромного опыта 1С-программинга...
З.Ы. Несколько лет назад 1С даже рассылала некий бюллетень ошибок, найденных пользователями,
правда быстро прекратила - после того, видимо, как его объем вырос за примерно полгода до нескольких сотен строк... :)
18. Альтаир (Altair777) 12.04.11 10:42
vkr пишет:
Не стоило бы, наверное, так строго судить... Автору - спасибо за заботу, 1С-ке - минус за глюки!

Зачем изначально программировать плохо? То что в 8-ке такая байда проходит, не делает чести ни ей, ни неряшливому программиста.
А вдруг на смену 1С-ке придет что-то лучшее? И тяжело будет этому кодеру писать правильно.
19. Александр Рыжов (AleksR) 12.04.11 12:52
(18) В 1С 7.7 приходилось писать:
--------------------
Обрабатывать=0;
Если Док.Вид()="ПриходнаяНакладная" Тогда
Если Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя Тогда
Обрабатывать=1;
КонецЕсли;
ИначеЕсли Док.Вид()="РасходнаяНакладная" Тогда
Если Док.ПризнакНакладной=Перечисление.ПризнРасхНакл.ВозвратПоставщику Тогда
Обрабатывать=1;
КонецЕсли;
КонецЕсли;
Если Обрабатывать=1 Тогда
...
КонецЕсли;
--------------------

А в 1С 8 можно написать:
--------------------
Если ((Док.Вид()="ПриходнаяНакладная") И (Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя))
ИЛИ ((Док.Вид()="РасходнаяНакладная") И (Док.ПризнакНакладной=Перечисление.ПризнРасхНакл.ВозвратПоставщику))
Тогда
...
КонецЕсли;
--------------------

Вы действительно считаете второй вариант "неряшливым"? И действительно в 1С 8 всегда создаётеь громаздьё конструкций "Если"?
20. Александр Рытов (Арчибальд) 12.04.11 13:35
(19) Я просто считаю, что логические операции И и ИЛИ коммутативны. И в семерке это так. Вы же вместе с разработчиками платформы восьмерки это отрицаете.
21. Александр Рыжов (AleksR) 12.04.11 13:56
(20) Подход к вычислению логических выражений, реализованный в 1С 8, кроме повышения быстродействия, позволяет создавать более короткий и более "читаемый" код. Пример я привёл выше. Кстати, это вовсе не изобретение 1С 8. В MS Fortran и FoxPro, на которых я работал до 1С, был реализован точно такой же подход. Поэтому для меня "лишние" вычисления в логическом выражении оказались в 1С 7 неприятным сюрпризом.
22. Альтаир (Altair777) 12.04.11 14:23
(19) не надо путать громоздкость кода с неряшливым подходом к вычисляемым условиям
23. Александр Рытов (Арчибальд) 12.04.11 15:10
(21) Да, код получается более коротким. Но не более читаемым - наша дискуссия тому доказательство.
Этот способ сокращения записи имеет право на существование, однако ж при чтении надо понимать, что краткость достигается за счет правильного понимания ненаписанного. А именно: в логических выражениях в восьмерке (и ряде других языков) отключен (частично) контроль корректности вычислений. Это фича (особенность реализации) таких языков, специально упоминаемая в описании языка. Отсутствие такого упоминания автоматически означает, что типы операндов контролируются путем вычисления.
На этапе составлении алгоритма я считаю некорректным использование особенностей реализации языка - тогда из "программиста" превращаешься в "программиста 1С8" или "программиста ФоксПро". Ну, а в первом примере "программист" и "программист 1С7.7" совпадают.
24. Franchiser (Franchiser) 12.04.11 17:12
Я тоже встречал ошибки: 1с по-разному работает под sql и под dbf:
- На скуле некорреткно работают запросы когда в них выбираются через точку реквизиты какого-либо справочника, причем в запросе обращение к этому справочнику стоит 2 и более раз.
- Почему то по оборотным счетам в базе на скуле хранятся в том числе и остатки.
25. Борис Скворцов (gaglo) 13.04.11 10:24
(23) Дискуссия идет "вот уже пятые сутки", и я не вытерпел...
Арчибальд пишет:
В логических выражениях в восьмерке (и ряде других языков) отключен (частично) контроль корректности вычислений.
Это фича (особенность реализации) таких языков, специально упоминаемая в описании языка.
Отсутствие такого упоминания автоматически означает, что типы операндов контролируются путем вычисления.
На этапе составлении алгоритма я считаю некорректным использование особенностей реализации языка

Считаю, что спор пошел уже совсем ни о чем не о том, поскольку:
- "ряд других языков" есть на деле большинство языков; ИМХО неполное вычисление логического выражения применяется в 1С8, Фортране, С и С++, полное в 1С7 и Visual Basic, счет 4:2;
- не "отключен контроль корректности вычислений", а отсутствует строгая типизация и широко распространено приведение типов, в том числе и неявное, из-за чего о контроле корректности вычислений без выполнения этих самых вычислений лучше не вспоминать;
- упоминание о таком способе вычисления логических выражений как о фиче в отношении языка 1С8 имеется, что было показано в посте 14;
- этап составления алгоритма уж давно прошел, а на этапе написания программы на конкретном языке правильное использование особенностей реализации этого языка очень даже похвально;
- и, кстати, на коммутативность логических операций это никоим боком не посягает, а выражение "(1=1) ИЛИ 8" синтаксически абсолютно верно, и только семантически с ним (возможно) что-то не так.
26. Виталий (nafa) 13.04.11 12:19
(25)
- "ряд других языков" есть на деле большинство языков; ИМХО неполное вычисление логического выражения применяется в 1С8, Фортране, С и С++, полное в 1С7 и Visual Basic, счет 4:2;

Более правильно сказать, что в ряде компиляторов (именно компиляторов, а не языков) - MS VC++, C++ Builder и т.п. есть настройка (включаемая пользователем), которая позволяет вычислять логические выражения неполностью.
Так как вычисление полностью нередко бывает необходимо на этапе отладки программ, чтобы максимально быстро выявить все ошибки.
27. Епрст (Ёпрст) 13.04.11 13:12
А вот когда автор узнает про * в параметрах сортировки, то не будет писать бреда типа этого:

Табл.Сортировать("Клиент,КлиентКод");
Арчибальд; Altair777; +2 Ответить 1
28. Епрст (Ёпрст) 13.04.11 13:15
+27 и тем более, не нужно лепить новую колонку для сортировки.
29. Евгений Долиновский (Dolly_EV) 13.04.11 13:30
Как всегда, в таких статьях - комменты рулят!)

Обход проблемы в п. 4:
"Условие (ВыбКлиент.Принадлежит(Клиент)=1);"
30. Альтаир (Altair777) 13.04.11 13:38
gaglo пишет:
- "ряд других языков" есть на деле большинство языков; ИМХО неполное вычисление логического выражения применяется в 1С8, Фортране, С и С++, полное в 1С7 и Visual Basic, счет 4:2;

1. При чем тут счет?!
2. Вы всего о 6 языках слышали? :D
31. Александр Рыжов (AleksR) 13.04.11 14:00
Dolly_EV пишет:
Обход проблемы в п. 4:
"Условие (ВыбКлиент.Принадлежит(Клиент)=1);"

Так нельзя. Общее правило для фильтров: если он не установлен (список значений ВыбКлиент пустой), то условие должно быть истинно.
32. Александр Рыжов (AleksR) 13.04.11 14:22
(27) Цель - отсортировать таблицу значений по наименованию, но так чтобы не оказались строки с разными элементами справочника, но одинаковыми наименованиями вперемешку. А при сортировке по внутреннему значению получим рядом все строки, относящиеся к одному элементу справочника, но упорядочивания по элементам вообще никакого не будет.
33. Епрст (Ёпрст) 13.04.11 15:47
(32) Ё.. как дети.

Табл.Сортировать("Клиент+,Клиент*");
34. Александр Рыжов (AleksR) 13.04.11 15:59
(33) Согласен. Так действительно лучше.
Это, на мой взгляд, первое дельное замечание по содержанию статьи.
35. Епрст (Ёпрст) 13.04.11 16:59
>>>3. Ошибка запроса по месяцам

Брехня.
Прикрепленные файлы:
_месяц.ert
36. Епрст (Ёпрст) 13.04.11 16:59
37. Епрст (Ёпрст) 13.04.11 17:02
>>>4.
Аналогично - всё работает.

ЗЫ: максимум, на SQL базе не всегда работает - а именно, если в списке 1 элемент , то фильтр игнорируется.
38. Епрст (Ёпрст) 13.04.11 17:16
+37
>>7.
Тоже бред. Функция всегда рассчитывается верно, если статус - измерение регистра.
Мот ты условия на реквизит регистра накладываешь ?
:))))))))))

Единственное - в запросе используется упрощенная схема проверки условий, если первый операнд вернул ложь, второй уже не проверяется ( в отличие от обычных условий)

>>>8. Ошибка запроса по нескольким регистрам с ресурсами разной точности

Тоже бред.
Никакой ошибки нет.
Запрос для вычисления функции берет значение из первого аргумента, объявленного как переменная текста запроса.
Если при расчете функций вообще не используются переменные текста запроса, например
|Функция Сумма = Сумма(ПолучитьСуммуИзВнешнейФункции(Товар)); тогда точность будет нулевой.
Решение - введение переменной нужной точности в вычисление функции:
|Количество = Регистр.Нужный.Количество;
|Функция Сумма = Сумма(Количество-Количество+ПолучитьСуммуИзВнешнейФункции(Товар)); //точность будет такой, как у переменной Количество.

>>>9. Ошибка запроса с отрицанием вхождения в список в условии Когда для функции
Тоже бред
пустое значение всегда принадлежит любому множеству.

Дальше не читал, ибо лень.
dimisa; Altair777; +2 Ответить 2
39. Альтаир (Altair777) 13.04.11 17:47
Ёпрст, где ты раньше был? :)
Ёпрст; +1 Ответить
40. Александр Рыжов (AleksR) 13.04.11 17:48
Ёпрст пишет:
>>>3. Ошибка запроса по месяцам

Брехня.

Отсутствие ошибки в непонятно каком релизе платформы в непонятно каком формате базы вовсе не означает, что её не было в 17-м релизе DBF-базы. Не исключено также, что ошибка может наблюдаться при дополнительных условиях, которые были в реальном запросе.
41. Епрст (Ёпрст) 13.04.11 17:55
(40) з@ебись .. Мот тебе еще и все ошибки всех древних релизов перечислить ?
Их есть у меня.
42. Альтаир (Altair777) 13.04.11 17:57
(40)
AleksR пишет:
что её не было в 17-м релизе DBF-базы

а в в 25 или 27 релизе она есть?
43. Александр Рыжов (AleksR) 13.04.11 17:59
(38) Это чего - теоретические рассуждения? Логических ошибок в этих запросах нет - они есть в реализации на платформе 1С 7.7 и конкретно наблюдались в 25 релизе на SQL-базе.

пустое значение всегда принадлежит любому множеству

А об этом я уже писал:

Функция ПрихДолг = Приход(ДолгПокуп) Когда (НЕ(КодОперации в СписокКодовОпераций));

Функция ПрихДолг = Приход(ДолгПокуп) Когда (КодОперации в СписокКодовОпераций);

Если пустое значение не входит в список СписокКодовОпераций, то функция для пустого значения КодОперации не выполняется ни в первом, ни во втором случае.

Это не в теории, а в конкретной практике.
44. Альтаир (Altair777) 13.04.11 18:05
По поводу того какая проблема к какому релизу - имхо лучше это написать не в шапке, а прямо в проблеме.

(43)
AleksR пишет:
Это не в теории, а в конкретной практике

Если практика конкретная - проблема 1-5 осталась в 25 релизе? а в 27?

P.S. Очень хочется снести эту публикацию - по-моему, от нее вреда больше чем пользы.
Жалко только что комменты Арчибальда и Ёпрста погибнут вместе с ней.
45. Епрст (Ёпрст) 13.04.11 18:09
А об этом я уже писал:

Функция ПрихДолг = Приход(ДолгПокуп) Когда (НЕ(КодОперации в СписокКодовОпераций));

Функция ПрихДолг = Приход(ДолгПокуп) Когда (КодОперации в СписокКодовОпераций);

Если пустое значение не входит в список СписокКодовОпераций, то функция для пустого значения КодОперации не выполняется ни в первом, ни во втором случае.

Это не в теории, а в конкретной практике.


Тебе еще раз говорят - пустое значение является элементом любого множества.
Да и, открой профайлер наконец.
46. Александр Рыжов (AleksR) 13.04.11 18:09
(40) В самом начале статьи я написал, какая проблема обнаружена в каком релизе и в каком формате базы, и предложил проверить всем желающим актуальность для последнего релиза.
47. Александр Рыжов (AleksR) 13.04.11 18:12
(45) А тебе ещё раз говорят: в конкретной практике "функция для пустого значения КодОперации не выполняется ни в первом, ни во втором случае".
48. Альтаир (Altair777) 13.04.11 18:13
(46) Мало кто видит для какого она релиза, это же надо каджый раз вверх подниматься
49. Александр Рыжов (AleksR) 13.04.11 18:16
(48) Я внесу уточнения в статью.
50. bulpi bulpi (bulpi) 13.04.11 21:30
Уважаемые Арчибальд и ему подобные!
Просто помните, что не все такие умные, как Вы.
Я это не в первый раз уже советую :D
Любовь к истине не должна приводить к ожесточению ... (типа мудрость :D )
51. Алексей Константинов (alexk-is) 14.04.11 07:06
Программный код в тексте публикации можно было раскрасить более простым способом :)
52. Александр Рытов (Арчибальд) 14.04.11 08:05
(50) Какое ожесточение? Ни я, ни Ёпрст (основные критики) минусом к статье не отметились, продемонстрировав тем самым свою толерантность. А вот автору следовало бы часть высказанных замечаний внести в текст статьи - не каждый полезет в обсуждение, и останется в результате весьма далек от истины.
53. Александр Рыжов (AleksR) 14.04.11 08:59
(52)
А вот автору следовало бы часть высказанных замечаний внести в текст статьи - не каждый полезет в обсуждение, и останется в результате весьма далек от истины.

Я в ближайшее время собираюсь сделать вариант статьи с дополнительными пояснениями. Однако ложных высказываний в статье пока не обнаружено.
54. Александр Рыжов (AleksR) 14.04.11 09:10
(38)
пустое значение всегда принадлежит любому множеству

Это неверное утверждение. В математике правильно так: пустое множество является подмножеством любого множество.
Причём пустое множество - это множество, не содержащее ни одного элемента.
Таким образом, элемент со значением 0 не принадлежит множеству {1; 2; 3}. Полный аналог пустого множество - это NULL в 1С 8.
55. Александр Рыжов (AleksR) 14.04.11 09:24
(51) А я и раскрашивал с помощью Вашей обработки "Разукрашка". Очень удобно. Спасибо ещё раз! Просто я результат раскраски вставлял в статью в Word. А уже всю статью из Word через буфер обмена копировал в поле описания публикации.
56. Алексей Константинов (alexk-is) 14.04.11 09:42
(55) Вот я и смотрю, что код HTML страшнее страшного.
57. Александр Рытов (Арчибальд) 14.04.11 10:06
(54) ЖКК, стр. 823:
Оператор языка запросов "Условие (А в Б);" говорит о том, что условие истинно, когда значение А является подмножеством значения Б.
Речь идет именно о двух множествах, одно из которых является/не является подмножеством другого, а не о вхождении элемента в множество.
Разумеется, описание невнятное. Но это лишь повод для того, чтобы программист обеспечил однозначность толкования вхождения путем контроля А и Б.
Пример: ПолучитьПустоеЗначение() принадлежит {1; 2; 3}, а ПолучитьПустоеЗначение("Число") - не принадлежит. ПолучитьПустоеЗначение() - это полный аналог NULL в 1С 8...
58. Александр Рыжов (AleksR) 14.04.11 10:29
(57) В пункте 9 статьи, о котором и идёт спор, на вхождение в список проверяется КодОперации, являющийся реквизитом регистра. И он имеет вполне определённый тип. И проверяться на вхождение будет не абстрактное ПолучитьПустоеЗначение(), а вполне конкретный " " (пробел), если КодОперации имеет тип строки длиной 1.
59. Александр Рытов (Арчибальд) 14.04.11 10:53
(58) А вот в этом конкретном месте ляп имеется.
В ЖКК имеется указание на то, что для строк ПустоеЗначение() работает как ПустаяСтрока(). На мой взгляд, эта фича весьма вредна. Разумным было бы результатом функции ПустоеЗначение(" ") иметь 0. Хотя бы потому, что СтрДлина() прекрасно считает пробелы.
Скуль, насколько мне известно (не уверен), пробельную строку пустым значением не считает.
60. Александр Рыжов (AleksR) 14.04.11 11:02
(59) В чём ляп? Как вы сами согласились, 1С считает пробельную строку тоже пустым значением. Поэтому в статье я и употреблял этот термин, как наиболее общий. Не важно, какой тип у реквизита КодОперации. Но для каждого типа есть своё пустое значение. И это не NULL в данном примере.
61. Александр Рытов (Арчибальд) 14.04.11 11:48
(60)
Но для каждого типа есть своё пустое значение.
В данном случае, не одно пустое значение.
ПустоеЗначение(" ") = 1
но
" " <> ПолучитьПустоеЗначение("Строка");

Вот я и считаю, что здесь 1С допустила ляп.
62. Александр Рыжов (AleksR) 14.04.11 12:29
Я согласен, что 1С достаточно вольно обращается с пробелами. Но речь-то идёт о другом. Согласитесь всё же, что Ваше замечание:
Девятый и десятый примеры. Всякому известно, что пустое множество является подмножеством любого множества. А в ЖКК условие вхождения описывается именно в терминах множеств.

некорректно по сути, так как в 9-м и 10-м примерах нет пустого множества, а есть пустые значения и это не одно и то же.
63. Александр Рытов (Арчибальд) 14.04.11 13:26
(62) Потому я в 57 посте и процитировал ЖКК: "Значение А является подмножеством значения Б". Очень коряво. Когда Б - СписокЗначений, напрашивается вывод, что и А должно как-то преобразовываться в список. И еще вопрос, если А - пустое значение, будет ли оно преобразовано в пустой список, либо в список, состоящий из одного пустого значения. К тому же списки длины 1 вообще ведут себя в запросах коряво.
64. Александр Рыжов (AleksR) 14.04.11 13:51
(63) Если и преобразуется, то в список из одного пустого значения. Как я уже дважды писал:
Функция ПрихДолг = Приход(ДолгПокуп) Когда (КодОперации в СписокКодовОпераций);
Если пустое значение не входит в список СписокКодовОпераций, то функция для пустого значения КодОперации не выполняется. И это, на мой взгляд, не ошибка 1С. А ошибка, когда при условии "НЕ(КодОперации в СписокКодовОпераций)" тоже не выполняется. И в ряде других запросов, которые я приводил, проблемы происходят при условии отрицания. В 10-м пункте я описал реальную ситуацию, с которой столкнулся, когда не выполняются лишь некоторые функции и лишь при некоторых условиях. Когда же отрицания нет, то с такими проблемами я не сталкивался.
65. Александр Рытов (Арчибальд) 14.04.11 14:50
Не поленился проверить:
Б = СоздатьОбъект("СписокЗначений");
Б.ДобавитьЗначение(1);
Б.ДобавитьЗначение(2);
Б.ДобавитьЗначение(0);
А = 0;

Условие (А в Б) = Ложь

А = ПолучитьПустоеЗначение("Число");

Условие (А в Б) = Ложь

А = ПолучитьПустоеЗначение();

Условие (А в Б) = Истина

Вот так как то... Для ДБФ. А в скуле - не так...
66. Александр Рыжов (AleksR) 20.04.11 14:09
(65) А как именно Вы проверяли Условие (А в Б)? Я проверил то же самое с помощью метода Принадлежит():
Б.Принадлежит(А)
и получил противоположный результат:
значение принадлежит данном списку в первых двух случаях (когда А=0 и А=ПолучитьПустоеЗначение("Число")) и НЕ принадлежит в последнем (когда А=ПолучитьПустоеЗначение()).
Если же в списке нет элемента со значением 0, тогда все три вышеуказанные значения НЕ принадлежат списку.

Из описания встроенного языка системы 1С:Предприятие 7.7:
"Метод Принадлежит проверяет вхождение в список значений заданного значения. Другими словами, оператор Список.Принадлежит(ВыбДок) проверяет, является ли значение ВыбДок подмножеством списка значений Список."

Таким образом, ПолучитьПустоеЗначение() тоже не является пустым множеством. Согласно документации, это пустое значение неопределённого типа.

Проверялось в релизе 27 и формате базы данных DBF.
67. Александр Рыжов (AleksR) 20.04.11 17:38
(65) +66 Сейчас потестил как запрос работает для двух вариантов условий:
Условие (А в Б);
Условие (НЕ(А в Б));
для двух вариантов списка значений Б:
{1; 2; 0}
{1; 2}
и для трёх вариантов значений А:
0
ПолучитьПустоеЗначение("Число")
ПолучитьПустоеЗначение()

Для значений 0 и ПолучитьПустоеЗначение("Число") все результаты одинаковы, что подтверждает эквивалентность этих значений. Дальше для краткости буду писать только про значение 0.
Из результатов запросов следует, что с точки зрения реализации запроса значение 0 не является подмножеством множества {1; 2}, что, на мой взгляд, логично. Однако, как Вы и писали, оно также не является подмножеством множества {1; 2; 0}, что уже совсем нелогично. Для значения ПолучитьПустоеЗначение() получается вообще полный бред: условие истинно для всех четырёх вариантов, т.е. ПолучитьПустоеЗначение() является подмножеством обоих множеств (это можно было бы трактовать как то, что ПолучитьПустоеЗначение() - это пустое множество), однако одновременно оно НЕ является подмножеством этих множеств (условия с НЕ тоже истинны)! Похоже, что фирма 1С в платформе 7.7 в запросах с пустыми значениями основательно накосячила.

Проверялось в релизе 27 и формате базы данных DBF.
68. Александр Рыжов (AleksR) 21.04.11 11:38
(65) +67 Дальнейшее тестирование показало, что запрос некорректно работает, когда в условии А является внешней переменной, а Б внешним списком значений:
Условие (А в Б);
Условие (НЕ(А в Б));
тогда при любых числовых и строковых значениях А (на других типах не проверялось) всегда в запросе считается, что А не является подмножеством Б, т.е. первое условие всегда ложно, а второе всегда истинно, даже для случаев:
А=2 и Б={1,2}
А="2" и Б={"1","2"}
Исключение - А=ПолучитьПустоеЗначение() и А="", тогда возникает неопределённость: оба условия всегда истинны.
Если же условие на проверку равенства (Б здесь не список значений, а переменная):
Условие (А = Б);
Условие (НЕ(А = Б));
Условие (А <> Б);
тогда запрос работает корректно с учётом типа значения, в том числе считается, что ПолучитьПустоеЗначение()<>"" и ""<>" "

Метод Принадлежит списка значений:
Б.Принадлежит(А)
также работает корректно с учётом типа, в том числе значение "" принадлежит списку, только если в него входит именно это значение (различается "" и " "). Значение ПолучитьПустоеЗначение() никогда не принадлежит списку, так как в него даже невозможно это значение добавить.

Проверялось в релизе 27 и формате базы данных DBF.
Арчибальд; +1 Ответить 1
69. Александр Рытов (Арчибальд) 21.04.11 11:46
(68) В итоге мы публику совсем запутали :D
70. Игорь Исхаков (Ish_2) 21.04.11 12:32
(69) Арчибальд ты пишешь, комментируя первый пример :
"Автор по-видимому считает, что при логическом умножении логического значения "Ложь" на что угодно, хоть на стеариновую свечку, должна таки ложь получиться."

Ты ,помнится, в "лохматые" годы работал на клиппере. (Clipper Summer 87 - вышел 24 года назад).
Ты не помнишь как тогда ,в дремучих 80-х , вычислялись логические выражения ?
Действительно, Если в выражении "А и В" определено ,что А= false ,то вычислялось ли В ?
71. Александр Рыжов (AleksR) 21.04.11 14:12
(69) Сейчас распутаем :)
Пост 68 показывает новую ошибку 1С 7.7 для случая запросов с условием вхождения в список значений внешней переменной. Я добавлю это в статью отдельным пунктом.
В пунктах же 9-м и 10-м в условии участвует внутренняя переменная запроса. Я сейчас протестировал (релиз 27, формат базы DBF) простые запросы типа:
Рекв=Справочник.МойСправочник.МойРеквизит;
Условие (Рекв в Б);
и типа:
Рекв=Справочник.МойСправочник.МойРеквизит;
Условие (НЕ(Рекв в Б));
Как я и ожидал, получилось следующее:
Если реквизит справочника МойРеквизит имеет числовой тип и значение 0, то он в запросе считается подмножеством списка Б (условие первого запроса даёт ИСТИНА, а второго - ЛОЖЬ) только тогда, когда в список значений добавлено значение 0.
Если реквизит справочника МойРеквизит имеет строковый тип и значение в виде пустой (пробельной) строки, то он в запросе является подмножеством списка Б только тогда, когда в список добавлена пустая строка той же длины, т.е. проверка производится на точное совпадение.
Таким образом, ещё раз подтверждено, что пустые значения НЕ воспринимаются в запросах как пустые множества.
72. Александр Рытов (Арчибальд) 22.04.11 10:03
(70) Все я помню. В Клиппере тоже использовалось, так называемое, сокращенное выполнение логических выражений, и это обстоятельство подчеркивалось в описании. Я говорю о том, что "фичей" я вляется именно сокращенное, а не полное вычисление. А полное вычисление - классическая норма. Программист имеет право пользоваться этим сокращением, но при этом он должен осознавать, что это именно фича.
73. Игорь Исхаков (Ish_2) 22.04.11 10:11
74. Александр Рыжов (AleksR) 26.04.11 09:10
Обновлена статья на сайте. Некоторые пункты протестированы в релизе 27 платформы 1С 7.7. В статье даны дополнительные пояснения и уточнения, добавлены 2 новых пункта с ошибками, обнаруженными в релизе 27.
75. Алексей (alexqc) 27.04.11 13:17
(72) Ну например в том же С и всех его наследниках сокращенное вычисление логики является именно нормой (не отключаемой :) ). Более того, именно на этом факте основаны приемы типа такой цепочки:

someAct1() && someAct2() && someAct3()

- выполнение последовательно нескольких действий в случае успеха каждого из них; и такая запись много проще и нагляднее лестницы из кучи if-ов. Что же касается квалификации прогера - так в тех языках очень много завязано на side-эффектах, и для прогеров подобные фичи счтиаются базовыми знаниями. Так собственно, почему бы в 8ке не появиться такой же практике?

Что же касается (А>Б И 8) - то как раз для 8ки это корректное выражение, и (8 И А>Б) - также корректное. А дело все в том, что тут числовой тип лЯгко приводится к логическому - по тем же сишным правилам: =0 - ложь, <>0 - истина.
Потому в 8ке можно писать например такое:

Если ТЗ.Количество() Тогда ...
76. Александр Рытов (Арчибальд) 27.04.11 13:47
(75) При чем здесь С? В нем вообще нет логического типа данных! Еще Бейсик бы вспомнил.
Тут вообще-то ООП обсуждается.
77. Алексей (alexqc) 27.04.11 13:57
(76) Я написал "С и наследники".
В том что в С++ есть ООП, надеюсь, не сомневаемся?
И, кстати, bool-тип там тоже есть.
Да и обсуждаем вроде не ООП, а логику, что к ООП вообщето перпендикулярно. И да, то что С нет выделенного логического типа никак не убирает из него работу с логикой.

А бейсик (VB?) тут не пойдет - там, как уже говорили выше, полное вычисление операций.
78. Олег Валуйский (waol) 03.11.11 14:14
AleksR пишет:

(18) В 1С 7.7 приходилось писать:

--------------------

Обрабатывать=0;

Если Док.Вид()="ПриходнаяНакладная" Тогда

Если Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя Тогда

Обрабатывать=1;

КонецЕсли;

ИначеЕсли Док.Вид()="РасходнаяНакладная" Тогда

Если Док.ПризнакНакладной=Перечисление.ПризнРасхНакл.ВозвратПоставщику Тогда

Обрабатывать=1;

КонецЕсли;

КонецЕсли;

Если Обрабатывать=1 Тогда

...

КонецЕсли;

--------------------



А в 1С 8 можно написать:

--------------------

Если ((Док.Вид()="ПриходнаяНакладная") И (Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя))

ИЛИ ((Док.Вид()="РасходнаяНакладная") И (Док.ПризнакНакладной=Перечисление.ПризнРасхНакл.ВозвратПоставщику))

Тогда

...

КонецЕсли;

--------------------



Вы действительно считаете второй вариант "неряшливым"? И действительно в 1С 8 всегда создаётеь громаздьё конструкций "Если"?


если типзначения(?(Док.Вид()="ПриходнаяНакладная",?(Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя,1,"Нетути"),?(Док.Вид()="РасходнаяНакладная",?(Док.ПризнакНакладной=Перечисление.ПризнРасхНакл.ВозвратПоставщику,1,"хренушки"),"ан не попал")))=1 тогда
КонецЕсли;
79. Аркадий Кучер (Abadonna) 23.11.11 03:08
(0)
или с проверками на неравенство значений и логическим оператором И, например:

Условие ((Статус<>УчетОказанныхУслуг) И (Статус<>Купленный) И (Статус<>Принятый));

А вот так не проверял?:
Условие (НЕ((Статус=УчетОказанныхУслуг) И (Статус=Купленный) И (Статус=Принятый)));
80. Александр Рыжов (AleksR) 23.11.11 08:52
(79) Так не проверял, поскольку многие косяки в реализации запросов 1С 7.7 фирма 1С допустила как раз в запросах с выражениями с отрицанием.
81. Александр Рыжов (AleksR) 23.11.11 08:58
(79) Как я понимаю, в своём выражении собирались написать ИЛИ, а не И.
82. Аркадий Кучер (Abadonna) 23.11.11 18:02
(81) Ну да... Недокопипастил ;)
А поводу вашего спора с Арчибальдом:
привычка писать в 8-ке
Если ((Док.Вид()="ПриходнаяНакладная") И (Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя))

может привести к ошибке в других языках.
Я стопудово буду "громоздить" Если, но напишу классически:
Если Док.Вид()="ПриходнаяНакладная" Тогда
Если Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя

Кстати, именно поэтому мне Си не нравится, с его синтаксическими вы@@@нами.
Вид
!(x>y && y>z)
меня даже чисто эстетически бесит ;)
Но на вкус и цвет...
83. DrZombi DrZombi (DrZombi) 26.11.12 09:18
Все лажа, Автору надо было просто написать...
1. 1С-ные запросы, или как их еще кличут "Черные" запросы, не предназначены для написания сложных запросов, где необходимо брать данные по нескольким "Таблицам" (Регистры, документы, справочники)
2. Если же Программисту, не судьба писать 20-тью "Простыми" запросами, то пиши те на прямых запросах: 1С++ вам в помощь.
3. Плюс не поставил, ибо нечего не ново и все обыденно и логично и без слов автора. Ибо даже при чтении описания языка запросов, там так то ни слова не сказано об возможности написания "Сложных" запросов.
84. Александр Рыжов (AleksR) 29.11.12 15:17
(83) DrZombi,
Ибо даже при чтении описания языка запросов, там так то ни слова не сказано об возможности написания "Сложных" запросов.

Вы очень невнимательно читали описание языка запросов, если вообще читали документацию 1С.
Цитирую руководство по описанию встроенного языка 1С:Предприяти 7.7 (Глава 34. Язык Запросов):
===============================================
В объявлении внутренней переменной можно указывать несколько вариан­тов <ОписанияПеременной>. Все описания должны указывать на один и тот же тип данных (число, строку, справочник или документ). Переменной, указы­вающей на разные справочники или документы присваивается тип данных «Справочник неопределенного вида» или «Документ неопределенного вида» соответственно.
* Например, можно определить внутреннюю переменную:
Товар = Документ.Перемещение.Товар, Документ.Расходная.Товар;
===============================================
И далее в этой главе идёт огромное количество примеров, когда данные берутся из нескольких таблиц.
Поэтому утверждение, что в 1С 7.7 не нужно писать сложные запросы, - неверно. Другое дело, что в некоторых случаях реализация запросов в платформе 7.7 сделана с ошибками. Но это относится и к очень простым запросам, см. например пункт 14 и 15 статьи.
85. Владимир Большов (BlueWind) 07.06.13 01:40
(33) AleksR, Ёпрст

Цель - отсортировать таблицу значений по наименованию, но так чтобы не оказались строки с разными элементами справочника, но одинаковыми наименованиями вперемешку.

Табл.Сортировать("Клиент+,Клиент*");


Долгое время использовал именно такой способ сортировки, не ожидая в нем подвоха.. Но он все же случился! ((

Есть таблица значений, содержащая данные по товарам и производителям. При не определенных пока обстоятельствах таблица, содержащая значения с совпадающим представлением, сортируется некорректно. Вот пример:
1с 7.7.25, база SQL. В таблице значений сортируется колонка Производитель (колонке назначен соответствующий тип Справочник.Контрагенты). В справочнике существует 2 элемента с наименованием "Россия ОАО Калина".

- Код 1. (Исходный). Сортировка таблицы значений:
тз.Сортировать("Производитель, *Производитель");

--- Результат (первые 20 элементов тз):
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700305) ( UC700)
Россия ОАО Калина (700305) ( UC700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700305) ( UC700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ISN "Марбиофарм" (700306) ( U8700)
Россия ISN "Марбиофарм" (700306) ( U8700)

= Элементы 700305 и 700337 не упорядочены.


- Код 2. Сортировка таблицы значений, содержащей единственную колонку Производитель. Исходный код заменен на следующий:
_тз = СоздатьОбъект("ТаблицаЗначений");
тз.Выгрузить(_тз,,,"Производитель");
тз = _тз;
тз.Сортировать("Производитель, *Производитель");
...Показать Скрыть

= Результат совпадает с результатом, полученным при выполнении исходного кода.
Пробовал с нетипизированной колонкой Производитель - результат тот же.


- Код 3. Сортировка индексированной таблицы, загруженной из таблицы значений. Исходный код заменен на следующий:
_итз = СоздатьОбъект("ИндексированнаяТаблица");
_итз.Загрузить(тз);
_итз.Сортировать("Производитель, *Производитель");
_итз.Выгрузить(тз);
...Показать Скрыть

--- Результат (первые 20 элементов тз):
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700305) ( UC700)
Россия ОАО Калина (700305) ( UC700)
Россия ОАО Калина (700305) ( UC700)
Россия ISN "Марбиофарм" (700306) ( U8700)
Россия ISN "Марбиофарм" (700306) ( U8700)

= Тут все в порядке.. Индексированная таблица с задачей справилась.


- Код 4. После сортировки, выполненной в индексированной таблице, выполняется сортировка таблицы значений. Исходный код заменен на следующий:
_итз = СоздатьОбъект("ИндексированнаяТаблица");
_итз.Загрузить(тз);
_итз.Сортировать("Производитель, *Производитель");
_итз.Выгрузить(тз);
тз.Сортировать("Производитель, *Производитель");
...Показать Скрыть

--- Результат (первые 20 элементов):
Россия ОАО Калина (700305) ( UC700)
Россия ОАО Калина (700305) ( UC700)
Россия ОАО Калина (700305) ( UC700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ОАО Калина (700337) ( SS700)
Россия ISN "Марбиофарм" (700306) ( U8700)
Россия ISN "Марбиофарм" (700306) ( U8700)

= Индексированная таблица помогла таблице значений упорядочить элементы, но сделала тз это все равно по-своему )

Вот такой вот капкан. Воспроизвести ситуацию, возможно, не получится, так как происходит такое при особом расположении звезд..
86. Кошки Рулят (Кошки рулят) 07.06.13 04:34
Статья - так себе, в основном, бояны не принципиальные, коменты - полный отстой.
Мои 5 копеек в коллекцию фич:
Метод списка значений Установить().
СП утверждает:
Установить(<?>,);
Синтаксис:
Установить(<Строка>,<Знач>)
Назначение:
Устанавливает в списке значение с указанным представлением. Если значение с таким представлением уже есть - изменяется значение, если нет - добавляется в конец списка значение с указанным представлением.
Параметры:
<Строка> - строка с символьным представлением устанавливаемого значения;
<Знач> - значение, которое устанавливается в позиции;

Теперь честно не читая дальше ответьте: какой результат даст выполнение метода без второго параметра <Знач>?
	СЗ=СоздатьОбъект("СписокЗначений");
	СЗ.Установить("1");   

По идее, раз значение не указано, для указанного представления будет установлено пустое значение.
	Сообщить(СЗ.РазмерСписка());  // 1 

Вроде все так - "Устанавливает в списке значение с указанным представлением."
А теперь честно не читая дальше ответьте: какой результат даст выполнение метода без второго параметра <Знач>, "если значение с таким представлением уже есть"?
	СЗ=СоздатьОбъект("СписокЗначений");
	СЗ.Установить("1"); 
	Сообщить(СЗ.РазмерСписка());  // 1
	СЗ.Установить("1"); 
	Сообщить(СЗ.РазмерСписка());  // ????
...Показать Скрыть

0
Интересно, кто-нибудь предвидел такой результат? Только честно?
Т.е., если метод применяется без второго параметра и при этом в списке значение с таким представлением уже есть, то значение с указанным представлением будет не установлено (пустым значением), вместо этого значение с указанным представлением будет удалено(?!) из списка. При этом, было ли у указанного представления установлено ранее какое-либо значение или нет, не имеет значения - значение все равно будет удалено из списка.
87. Александр Рыжов (AleksR) 07.06.13 09:51
(85) BlueWind,
Судя по команде СоздатьОбъект("ИндексированнаяТаблица"), у Вас используется компонента 1С++. Возможно, именно она нарушает правильную сортировку таблицы значений без индексации. Я сталкивался с тем, что подключение сторонних компонент, может нарушать даже работу того, на что компонента по идее вообще не должна оказывать влияния.
88. Владимир Большов (BlueWind) 07.06.13 14:37
(87) AleksR,

На 1с++ тоже падало подозрение. Отключил внешние компоненты, заполнение тз выполнил перебором документов, выполнил код:

тз.Сортировать("Производитель, *Производитель");

--- Результат:
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700305) ({"B","0","0","1110","0","0"," 1092700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ОАО Калина (700305) ({"B","0","0","1110","0","0"," 1092700"})
Россия ОАО Калина (700305) ({"B","0","0","1110","0","0"," 1092700"})
Россия ОАО Калина (700337) ({"B","0","0","1110","0","0"," 1036700"})
Россия ISN "Марбиофарм" (700306) ({"B","0","0","1110","0","0"," 1088700"})

= Элементы 700305 и 700337 не упорядочены. Влияние внешней компоненты, таким образом, исключается.

Сделал выгрузку из SQL в DBF - результат получен точно такой же (без ВК)...
89. Владимир Большов (BlueWind) 07.06.13 15:27
(86) Кошки рулят,

СЗ=СоздатьОбъект("СписокЗначений");
СЗ.Установить("1");
Сообщить(СЗ.РазмерСписка()); // 1
СЗ.Установить("1");
Сообщить(СЗ.РазмерСписка()); // ????

При опущенном параметре <Знач> ожидается, что существующее значение с таким представлением будет удалено из списка. Если это и не документировано, то используется изначально - в стандартных отчетах по бухгалтерии, написанных 1с где-то на границе веков, именно так выполняется удаление значений из расшифровки при выходе из циклов (Расшифровка.Установить("Субконто"+Номер[1]);). Если при опущенном параметре <Знач> значение удаляется, то при отсутствии в списке значения с таким представлением ожидается, что список останется неизменным. В таком случае, появление в списке пустого значения по строке кода 2 выглядит странным. Интересно, что при явном указании параметра <Знач>, содержащего пустое значение, поведение 1с точно такое же:
   СЗ=СоздатьОбъект("СписокЗначений");
   СЗ.Установить("1", ПолучитьПустоеЗначение()); 
   Сообщить(СЗ.РазмерСписка());  // 1
   Если сз.РазмерСписка() <> 0 Тогда
	   Сообщить(?(сз.ПолучитьЗначение(1) = ПолучитьПустоеЗначение(),"Пустое","Не пустое")); //Пустое
   КонецЕсли;
   СЗ.Установить("1", ПолучитьПустоеЗначение()); 
   Сообщить(СЗ.РазмерСписка());  // 0
...Показать Скрыть
90. trade70 28.01.14 08:51
Однако в 1С 7 приходится в таком случае создавать более громоздкую и менее наглядную конструкцию:

Обрабатывать=0;
Если      Док.Вид()="ПриходнаяНакладная" Тогда
    Если Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя Тогда
        Обрабатывать=1;
    КонецЕсли;
ИначеЕсли Док.Вид()="РасходнаяНакладная" Тогда
    Если Док.ПризнакНакладной=Перечисление.ПризнРасхНакл.ВозвратПоставщику Тогда
        Обрабатывать=1;
    КонецЕсли;
КонецЕсли;
Если Обрабатывать=1 Тогда
...Показать Скрыть


Не проще ли так, это сразу бросается в глаза и наглядней)) В построение кода нужны еще и мозги. А не только языковые конструкции. Изучайте мат часть.

Если (Док.Вид()="ПриходнаяНакладная") Или (Док.Вид()="РасходнаяНакладная") Тогда
Если (Док.ПризнакНакладной=Перечисление.ПризнПрихНакл.ВозвратОтПокупателя) Или
(Док.ПризнакНакладной=Перечисление.ПризнРасхНакл.ВозвратПоставщику Тогда
Обрабатывать=1;
КонецЕсли;
КонецЕсли;
Прикрепленные файлы:
91. Елена Пименова (Bukaska) 28.01.14 10:04
Хорошая статья))) Интересно читать такого рода статьи, несмотря на то, что я ПолныйНуб в семерке)))
92. DrZombi DrZombi (DrZombi) 29.06.14 13:49
Всегда проверяй. Никогда не принимай на веру, то как описан тот или иной функционал в СП или в книжке от 1С :)
Лучше всего понимать, что в первозданном виде 1С писалось для ларька по торговле семечками :)
И все у вас получится
93. Влад Петроченко (TesterMe) 09.12.15 14:42
Исправьте пожалуйста статью.
Табл.Сортировать("Клиент+,Клиент*"); // работает тоже неверно

Получаем:
66699288 Тест
00004369 Тест
66703402 Тест
00004369 Тест
Табл.Сортировать("Клиент,КлиентКод"); // сортировать только так

Я при обходах таблиц по таблицам использую:
НомСтроки = "";
Если ТЗ.НайтиЗначение(ТЗКлиент.Клиент, НомСтроки, "Клиент") = 1 Тогда
	Для НомСтроки = НомСтроки По ТЗ.КоличествоСтрок() Цикл
		ТЗ.ПолучитьСтрокуПоНомеру(НомСтроки);
		Если ТЗ.Клиент = ТЗКлиент.Клиент Тогда
			// нужный нам код
		Иначе
			Если ТипЗначенияСтр(ТЗ.Клиент) = "Справочник" Тогда
				Если ТЗ.Клиент.Наименование <> ТЗКлиент.Клиент.Наименование Тогда
					Прервать;
				КонецЕсли;
			Иначе
				Прервать;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
КонецЕсли;
...Показать Скрыть
94. Александр Рыжов (AleksR) 11.12.15 12:16
Обновлена статья на сайте.
1. Изменён пример в пункте 1.
2. Внесены изменения в пункт 12 в связи с сообщениями BlueWind (85) и TesterMe (93) о проблемах с сортировкой по внутреннему значению элемента справочника.
3. Восстановлена раскраска кода.
95. Михаил Максимов (МихаилМ) 11.12.15 12:39
в 1с77 использование в выражении докумет.Вид() приводит к скрытому запросу. те тоже является "проблемной" реализацией.
и в описании другой "проблемной" реализации как-то неуместно.
96. Сергей (Che) Коцюра (CheBurator) 12.12.15 05:24
Когда-то давно наткунлся

Справочник.Чтото
Код - текстовый
код заполнен "числами" с ведущими нулями на всю длину кода

В запросе

|Код = Справочник.Чтото.Код;
|Функция СуммаКод=Сумма(Код);

дбф - нормально, скуль - ошибка...
97. Александр Рыжов (AleksR) 12.12.15 18:38
98. Сергей (Che) Коцюра (CheBurator) 06.01.16 19:05
Еще

Метод количествострок() на уменьшение, после сортировки глючит НЕЗАВИСИМО от использования компонент, и глючит всегда.
Обрезает так, как будто не было сортировки.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа