t-sql В случае возможен другой тип данных?

у меня есть этот запрос

SELECT
CASE WHEN dbo.CFE_PPHY.P77 IS NOT NULL OR dbo.CFE_PPHY.P77 <>'' 
       THEN MONTH(dbo.CFE_PPHY.P77)
     WHEN dbo.CFE_PPHY.P70 IS NOT NULL OR dbo.CFE_PPHY.P70 <>'' 
       THEN MONTH(dbo.CFE_SERVICE_EVTS.C10_2)
     ELSE COALESCE(CONVERT(VARCHAR,dbo.CFE_PPHY.P77)+
          CONVERT(VARCHAR,dbo.CFE_SERVICE_EVTS.C10_2),'toto') END 
 AS CFELiasse_DateEffetEIRL_MM_N
FROM CFE_PPHY LEFT JOIN CFE_SERVICE_EVTS ON CFE_PPHY.colA = CFE_SERVICE_EVTS.colB

Часть ELSE вызывает у меня головную боль.

Столбцы CFE_PPHY.P77 и CFE_SERVICE_EVTS.C10_2 имеют формат даты и времени. Я превращаю их в varchar. Тем не менее, когда я запускаю запрос, у меня есть следующая ошибка

Сообщение 245, уровень 16, состояние 1, строка 1 Преобразование завершилось неудачно при преобразовании значения varchar 'toto' в тип данных int.

Очевидно, я не могу превратить toto в целое число. Справедливо. Однако, с моей точки зрения, я преобразовал формат даты и времени в формат varchar, поэтому он должен работать.

Где я не прав?

Спасибо


person Andy K    schedule 03.06.2016    source источник
comment
Выражение case возвращает значения для столбца. Первые две части вашего выражения case возвращают целые числа. МЕСЯЦ(). Вы не можете смешивать такие типы данных в одном столбце. Вам также придется преобразовать их в varchar. Вам также действительно нужно указать длину для varchars, а не просто использовать значение по умолчанию. По умолчанию 30 или 1 в зависимости от использования. Вы на 100% уверены, что знаете, что есть что? И если MS изменит, то в какой-то момент ваш код сломается.   -  person Sean Lange    schedule 03.06.2016
comment
Проблема в том, что CASE не поддерживает несколько типов данных, в ваших первых двух условиях вы возвращаете INT в форме MONTH(blah). Либо преобразуйте это в VARCHAR, либо не используйте toto.   -  person Chris Pickford    schedule 03.06.2016


Ответы (4)


Вы должны преобразовать все свои выражения case в varchar. SQL решает регистрировать поле как int, поэтому «toto» недопустимо. Если все выражения преобразованы в varchar, эта ошибка должна быть решена.

http://blog.sqlauthority.com/2010/10/08/sql-server-simple-explanation-of-data-type-precedence/

person Joe C    schedule 03.06.2016
comment
Привет @joe-c, твое объяснение было кристально ясным. Очки ваши, сэр. - person Andy K; 03.06.2016

Внимательно посмотрите на свое выражение case: в первой и второй условных ветвях вы возвращаете MONTH(..., которое, очевидно, является целым числом.

Но в третьей ветке вы возвращаете varchar, поэтому SQL-сервер пытается преобразовать его в int в соответствии с типом данных предыдущих ветвей и не может этого сделать.

person Andy Korneyev    schedule 03.06.2016

Попробуйте так,

SELECT CASE 
        WHEN dbo.CFE_PPHY.P77 IS NOT NULL
            OR dbo.CFE_PPHY.P77 <> ''
            THEN convert(VARCHAR, MONTH(dbo.CFE_PPHY.P77))
        WHEN dbo.CFE_PPHY.P70 IS NOT NULL
            OR dbo.CFE_PPHY.P70 <> ''
            THEN convert(VARCHAR, MONTH(dbo.CFE_SERVICE_EVTS.C10_2))
        ELSE COALESCE(CONVERT(VARCHAR, dbo.CFE_PPHY.P77) + CONVERT(VARCHAR, dbo.CFE_SERVICE_EVTS.C10_2), 'toto')
        END AS CFELiasse_DateEffetEIRL_MM_N
FROM CFE_PPHY
LEFT JOIN CFE_SERVICE_EVTS ON CFE_PPHY.colA = CFE_SERVICE_EVTS.colB
person StackUser    schedule 03.06.2016
comment
Привет, @stacknewuser, я хочу дать тебе баллы, но Джо Си пришел раньше. Спасибо за ваше объяснение. - person Andy K; 03.06.2016
comment
Пожалуйста. Я хочу исправить вашу проблему, не беспокоясь о баллах - person StackUser; 03.06.2016

Во-первых, при преобразовании в строку всегда указывайте длину (в SQL Server). Длина по умолчанию зависит от контекста и может быть неверной.

Во-вторых, сравнение значений даты/времени с '' не требуется. На самом деле это недопустимое значение для даты/времени, хотя оно преобразуется в 0, то есть 1900-01-01. Сравнение NULL должно быть достаточным. В противном случае, будьте явными.

В-третьих, конкатенация строк вернет NULL, если любой из аргументов равен NULL.

В-четвертых, псевдонимы таблиц облегчают написание и чтение запросов.

Насколько я могу судить, ваш case немного сложнее. В ELSE мы знаем, что dbo.CFE_PPHY.P77 есть NULL из-за первого условия. Итак, как насчет:

SELECT (CASE WHEN p.P77 IS NOT NULL
             THEN CAST(MONTH(p.P77) as VARCHAR(255))
             WHEN p.P70 IS NOT NULL
             THEN CAST(MONTH(e.C10_2) as VARCHAR(255))
             ELSE 'toto'
        END) AS CFELiasse_DateEffetEIRL_MM_N
FROM CFE_PPHY p LEFT JOIN
     CFE_SERVICE_EVTS e
     ON p.colA = e.colB;
person Gordon Linoff    schedule 03.06.2016
comment
Привет @gordon-linoff, спасибо за ваш ответ и приложенное обучение. Я продолжил работу над вашей книгой. Это очень интересно, я только что добрался до главы 3. Если подумать, мне нужно продолжить чтение, я сделаю это сегодня вечером. - person Andy K; 03.06.2016