При переходе на использование прямых SQL-запросов (например, через внешнюю компоненту 1С++ в версии 7.7 или через ADO в 8.x) программисты часто сталкиваются с синтаксическими ошибками, которые не всегда очевидны из текста ошибки. Одной из самых мощных и в то же время капризных конструкций является оператор CASE. В этой статье мы подробно разберем, как правильно внедрить логику ветвления в ваши запросы, почему возникают ошибки типа 0x80004005 и как корректно типизировать результат.
Рассмотрим ситуацию, с которой столкнулся автор темы. Изначально код выглядел так:
Select
TT.UnitPos,
Nomenclature =
CASE TT.AccType
WHEN 0 THEN TT.NomCompl
ELSE TT.Nomenclature
END,
TT.UnitsName
Проанализируем этот фрагмент. В стандартном SQL (T-SQL) для определения псевдонима (алиаса) поля чаще всего используется конструкция Выражение AS ИмяПоля. Хотя синтаксис ИмяПоля = Выражение поддерживается Microsoft SQL Server, он часто вызывает проблемы при парсинге запросов через драйверы ODBC или компоненты 1С. Кроме того, в сложных вложенных запросах такой синтаксис может привести к конфликтам.
Правильный подход: всегда указывать псевдоним после конструкции END. Это делает код более читаемым и стандартным для SQL-сервера.
SELECT
TT.UnitPos,
CASE
WHEN TT.AccType = 0 THEN TT.NomCompl
ELSE TT.Nomenclature
END AS Nomenclature,
TT.UnitsName
Выясним причину, по которой иногда "не взлетает" короткая запись. В SQL существует два способа написания этого оператора.
CASE TT.AccType
WHEN 0 THEN 'Тип 0'
WHEN 1 THEN 'Тип 1'
ELSE 'Неизвестно'
END
CASE
WHEN TT.AccType = 0 THEN 'Тип 0'
WHEN TT.AccType IS NULL THEN 'Пусто'
ELSE 'Другое'
END
Важно помнить: если вы используете поисковую форму (пишете WHEN сразу после CASE), вы обязаны указать полное условие сравнения, например, WHEN TT.AccType = 0. Ошибка в сообщении 31 (CASE WHEN 0 THEN 1...) возникла именно потому, что число 0 не является логическим выражением (True/False).
Рассмотрим подробнее важный нюанс, который часто упускают при использовании CASE. Все значения, возвращаемые в ветках THEN и ELSE, должны иметь один и тот же тип данных или типы, которые SQL Server может неявно преобразовать друг в друг.
Если TT.NomCompl — это строка (строковый идентификатор объекта 1С), а TT.Nomenclature — это, к примеру, число, запрос упадет с ошибкой преобразования типов. SQL Server всегда пытается привести все результаты к типу с наивысшим приоритетом. Чтобы избежать проблем, рекомендуется явно приводить типы с помощью функции CAST или CONVERT.
CASE
WHEN TT.AccType = 0 THEN CAST(TT.NomCompl AS VARCHAR(13))
ELSE CAST(TT.Nomenclature AS VARCHAR(13))
END AS Nomenclature
Проанализируем ситуацию с булевыми полями. В SQL Server нет типа Boolean, вместо него используется BIT. Значения в нем хранятся как 0 и 1.
Если вы работаете с базой 1С 7.7, то многие флаги (например, "ЭтоГруппа") хранятся именно в числовых или битовых полях. В запросах к ним нужно обращаться как к числам. Однако стоит учитывать, что при передаче параметров через 1С++ или ADO, иногда требуется указывать шестнадцатеричные значения, хотя в 99% случаев обычное сравнение = 0 или = 1 работает корректно.
Ошибка 0x80004005 — это общая ошибка (Unspecified error), которую возвращает ADO. Она не говорит о том, что именно сломалось, она лишь сообщает, что выполнение запроса прервано.
Чтобы найти истинную причину, выполним следующие шаги:
$Справочник.Номенклатура) подставьте реальные имена таблиц (SC123). Консоль выдаст точный номер строки и описание синтаксической ошибки.AS TT. Без алиаса вложенной таблицы SQL Server выдаст ошибку.FROM или пропущенная запятая между полями SELECT.Посмотрим на финальный вариант структуры запроса, который будет работать стабильно. Мы объединим логику выбора номенклатуры прямо во вложенном запросе, чтобы основной блок SELECT оставался чистым.
SELECT
TT.UnitPos,
TT.ParentPOS,
-- Используем CASE для выбора нужного наименования
CASE
WHEN TT.AccType = 0 THEN TT.NomCompl
ELSE TT.Nomenclature
END AS CorrectName,
TT.UnitsName
FROM (
SELECT
TElems.UnitPos AS UnitPos,
TElems.ParentPOS AS ParentPOS,
TNNomenclature.Name AS Nomenclature,
TNN.Name AS NomCompl,
TAccessories.AccType AS AccType
FROM
$Справочник.Элементы AS TElems
INNER JOIN
$Справочник.Номенклатура AS TNNomenclature ON TElems.PriceID = TNNomenclature.ID
LEFT JOIN
$Справочник.Аксессуары AS TAccessories ON TElems.UnitPos = TAccessories.UnitPos
LEFT JOIN
$Справочник.Номенклатура AS TNN ON TAccessories.AccMatID = TNN.ID
) AS TT
Важный совет: Если вы используете 1С++, старайтесь использовать метапарсер (символ $). Это позволит вам писать $Справочник.Номенклатура вместо гадания имен таблиц типа SC145. Это не только упрощает чтение, но и защищает ваш код от поломок при изменении структуры базы данных.
Конструкция CASE — это основной инструмент реализации бизнес-логики на стороне сервера базы данных. При соблюдении правил написания алиасов (AS Alias), контроле типов возвращаемых значений и проверке запросов в SSMS, вы сможете создавать производительные и надежные отчеты. Помните, что SQL требует строгости: каждое слово CASE должно иметь свое END, а каждое условие в WHEN должно возвращать булевый результат.