2016-11-09 4 views
2

Предположим, что у нас есть таблица, как показано ниже:SQL Server метаданные вычисляемого столбца в

CREATE TABLE dbo.tab(id INT PRIMARY KEY 
        -- other columns 
        ,is_active BIT); 

INSERT INTO dbo.tab(id, is_active) 
VALUES (1, NULL), (2, 1), (3,0); 

Дело в том, чтобы добавить вычисляемый столбец, который изменит NULL к 0 и вернуть исходные значения, когда это возможно.

Окончательный результат:

  • оригинальные колонны должны оставаться нетронутыми (так что нет никакой возможности UPDATE и ALTER таблицы)
  • вычисляемый столбец должен иметь правильный тип и допустимость пустых
  • не может использовать VIEW/TRIGGER/...

Итак, давайте просто добавим эту колонку:

CREATE TABLE dbo.tab(
    id INT PRIMARY KEY 
    ,is_active BIT 
    ,calc_flag1 AS CAST(IIF(is_active IS NULL,0 ,is_active) AS BIT) 

    ,calc_flag2 AS CAST(IIF(is_active IS NULL,0 ,ISNULL(is_active,0)) AS BIT) 
    ,calc_flag3 AS IIF(is_active IS NULL,0 , ISNULL(is_active,0)) 

    ,calc_flag4 AS CAST(ISNULL(IIF(is_active IS NULL,0 , is_active), 0) AS BIT) 
    ,calc_flag5 AS ISNULL(IIF(is_active IS NULL,0 ,is_active),0) 

    ,calc_flag6 AS ISNULL(CAST(IIF(is_active IS NULL,0 ,is_active) AS BIT), 
         CAST(0 AS BIT)) 
); 

LiveDemo

данных:

SELECT * FROM dbo.tab; 

╔══╦═════════╦══════════╦══════════╦══════════╦══════════╦══════════╦══════════╗ 
║id║is_active║calc_flag1║calc_flag2║calc_flag3║calc_flag4║calc_flag5║calc_flag6║ 
╠══╬═════════╬══════════╬══════════╬══════════╬══════════╬══════════╬══════════╣ 
║ 1║ NULL ║ False ║ False ║  0 ║ False ║  0 ║ False ║ 
║ 2║ True ║ True  ║ True  ║  1 ║ True  ║  1 ║ True  ║ 
║ 3║ False ║ False ║ False ║  0 ║ False ║  0 ║ False ║ 
╚══╩═════════╩══════════╩══════════╩══════════╩══════════╩══════════╩══════════╝ 

и метаданные проверка:

EXEC sp_help 'dbo.tab'; 

╔═════════════╦══════╦══════════╦════════╦══════╦═══════╦══════════╗ 
║ Column_name ║ Type ║ Computed ║ Length ║ Prec ║ Scale ║ Nullable ║ 
╠═════════════╬══════╬══════════╬════════╬══════╬═══════╬══════════╣ 
║ id   ║ int ║ no  ║  4 ║ 10 ║  0 ║ no  ║ 
║ is_active ║ bit ║ no  ║  1 ║  ║  ║ yes  ║ 
║ calc_flag1 ║ bit ║ yes  ║  1 ║  ║  ║ yes  ║ 
║ calc_flag2 ║ bit ║ yes  ║  1 ║  ║  ║ yes  ║ 
║ calc_flag3 ║ int ║ yes  ║  4 ║ 10 ║  0 ║ no  ║ 
║ calc_flag4 ║ bit ║ yes  ║  1 ║  ║  ║ yes  ║ 
║ calc_flag5 ║ int ║ yes  ║  4 ║ 10 ║  0 ║ no  ║ 
║ calc_flag6 ║ bit ║ yes  ║  1 ║  ║  ║ no  ║ 
╚═════════════╩══════╩══════════╩════════╩══════╩═══════╩══════════╝ 

Первая попытка:

,calc_flag1 AS CAST(IIF(is_active IS NULL,0 ,is_active) AS BIT) 

Исправьте тип данных, но он не может получить значение nullability. Я могу понять это потому, что он имеет жестко заданное значение и столбец с нулевым значением, поэтому все выражение оценивается как NULL.

Вторая попытка:

,calc_flag2 AS CAST(IIF(is_active IS NULL,0 ,ISNULL(is_active,0)) AS BIT) 

То же, что и раньше, но с явным ISNULL(is_active, 0). Теперь он должен работать, потому что есть hardcoded значение и ISNULL, но это не так.

,calc_flag3 AS IIF(is_active IS NULL,0 , ISNULL(is_active,0)) 

Что интересно, без CAST это получить nullable - no, но тип данных INT в настоящее время.

Третья попытка:

,calc_flag4 AS CAST(ISNULL(IIF(is_active IS NULL,0 , is_active), 0) AS BIT) 

Кастинг ISNULL когда второе значение жёстко. Почему это может быть nullable?

,calc_flag5 AS ISNULL(IIF(is_active IS NULL,0 ,is_active),0) 

Конечно, без приведения его работы, как это должно быть.

Заключительная попытка:

,calc_flag6 AS ISNULL(CAST(IIF(is_active IS NULL,0 ,is_active) AS BIT), 
         CAST(0 AS BIT)) 

Теперь я получаю правильный тип данных и допустимость пустых, но это как-то некрасиво.


Вопрос заключается в том, почему он ведет себя таким образом, и не могут получить правильные метаданные при calc_flag2 или calc_flag4 используется.

+1

Любопытный ... Я голосовал, чтобы закрыть и перейти на сайт дБА, где мы надеемся, Пол Уайт, или кто-то с подобным опытом на внутренностях SQL Server будет видеть это. Для чего вы можете получить столбец с недействительным битом, используя просто 'calc_flag7 AS ISNULL (is_active, 0)' – GarethD

+1

Я думаю, что проблема может быть упрощена до того, почему 'ISNULL (is_active, 0)' дает столбец с непустым значением столбца , но просто добавив преобразование, подобное 'CONVERT (BIT, ISNULL (is_active, 0))', приводит к тому, что один и тот же столбец имеет значение NULL. Используя специальный раздел из [этого ответа] (http://dba.stackexchange.com/a/114266/7257) (я знал, что у Пола Уайта будет ответ!), Причина в том, что некоторые сеансы настройки могут привести к переполнению конверсий return null, так что единственный способ обеспечить столбец с нулевым значением - это то, что внешняя самая большая функция - 'ISNULL'. – GarethD

+2

Еще одно замечание состоит в том, что выражение 'CASE' будет иметь только возвращаемый тип с нулевым значением, если ** все ** возвращаемые выражения не являются нулевыми, поэтому даже если вы используете' IIF (is_active IS NULL, 0, is_active) ', выражение return для false никогда не будет достигнуто, если оно равно null, оно по-прежнему является возвращаемым типом возвращаемого значения, поэтому возвращаемый тип является нулевым. – GarethD

ответ

1

Первое, что нужно отметить, что при использовании IIF (которая расширяется до CASE выражения за кадром), то результат будет только обнуляемым если все возвратные выражения не обнуляемым, поэтому, когда вы используете:

IIF(is_active IS NULL,0,is_active) 

Хотя логически, когда вы получаете is_active в выражении ложная она никогда не будет аннулирована из-за множества условий, это не имеет значения для компилятора, он просто может видеть, что один из возвращенных выражений is_active, который столбца с нулевым значением, поэтому возвращаемый тип является нулевым.

Я думаю, что проблема может быть упрощена до того, почему ISNULL (is_active, 0) дает столбец с недействительным битом, но просто добавляет конвертер типа CONVERT (BIT, ISNULL (is_active, 0)), вызывает тот же столбец может быть нулевым.

Быстрый Демо:

CREATE TABLE #tab(
    id INT PRIMARY KEY 
    ,is_active BIT 
    ,calc_flag1 AS ISNULL(is_active, 0) 
    ,calc_falg2 AS CONVERT(BIT, ISNULL(is_active, 0)) 
); 

EXECUTE tempdb.dbo.sp_help '#tab'; 

Что дает соответствующие результаты

Column_name Type Computed Nullable 
-------------------------------------------- 
id   int  no   no 
is_active bit  no   yes 
calc_flag1 bit  yes   no 
calc_falg2 bit  yes   yes 

Используя специальный раздел из this answer (Кредит на Paul White) причина в том, что некоторые настройки сеансов может вызвать переполнение преобразования для возврата null, поэтому единственный способ обеспечить столбец с нулевым значением - это то, что внешняя самая большая функция - ISNULL.

Необходимое решение может быть достигнуто просто с использованием ISNULL(is_active, 0), как показано выше, поскольку это возвращает столбец с недействительным битом, но стоит отметить, что если требуется преобразование, например, если вам нужно, чтобы он был столбцом int , то преобразование должно быть внутри ISNULL. Поскольку ISNULL вернет тип первого аргумента, требуется только одно преобразование, например.

CREATE TABLE #tab(
    id INT PRIMARY KEY 
    ,is_active BIT 
    ,calc_flag1 AS ISNULL(is_active, 0) 
    ,calc_falg2 AS CONVERT(BIT, ISNULL(is_active, 0)) 
    ,calc_flag_int AS ISNULL(CONVERT(INT, is_active), 0) 
); 

EXECUTE tempdb.dbo.sp_help '#tab'; 

Что дает соответствующие результаты

Column_name  Type Computed Nullable 
-------------------------------------------- 
id    int  no   no 
is_active  bit  no   yes 
calc_flag1  bit  yes   no 
calc_falg2  bit  yes   yes 
calc_falg_int int  yes   no 
2

Согласно MSDN, ...

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

Механизм базы данных автоматически определяет неопределенность вычисляемых столбцов на основе используемых выражений. Результат большинства выражений считается нулевым, даже если присутствуют только неисчислимые столбцы, потому что возможные потоки или переполнения будут также давать нулевые результаты. Используйте функцию COLUMNPROPERTY с свойством AllowsNull, чтобы исследовать нулеуемость любого вычисленного столбца в таблице. Выражение, которое является нулевым, может быть превращено в незанятое, указав ISNULL (check_expression, constant), где константа является ненулевым значением, заменяемым любым нулевым результатом.

Так что я бы сказал, что, так как ваш is_active поле обнуляемое, двигатель вычисляет, что все еще можно добраться до нулевого состояния, пока вы специально не защититься от них в конечном ISNULL.

Я собираюсь попытаться создать переполнение или нижнее течение, которое приведет к нулевому значению столбца бит после вставки, чтобы проверить механизм, но ваш вопрос действительно выглядит так, что выражение, которое у вас есть в вычисленном столбце, специально защищает используя оператор case.

 Смежные вопросы

  • Нет связанных вопросов^_^