Проверьте, не является ли ровно одна переменная нулевой - PullRequest
5 голосов
/ 07 апреля 2011

В триггере в моей базе данных sql-server 2008 мне нужно проверить, не является ли ровно одна переменная нулевой.Этот код делает то, что мне нужно, но можно ли сделать его в несколько строк и сделать его более читабельным?

DECLARE @string varchar
DECLARE @float float
DECLARE @bit bit
DECLARE @int int

Set @string=NULL  -- Exactly one of these variables needs to be set
Set @float=NULL   --
Set @bit=NULL     -- 
Set @int=NULL     --

IF(   (@string is not null AND COALESCE(@float, @bit, @int) IS NULL)
    OR (@float is not null AND COALESCE(@string, @bit, @int) IS NULL)
    OR (@bit is not null AND COALESCE(@string, @float, @int) IS NULL)
    OR (@int is not null AND COALESCE(@string, @float, @bit) IS NULL)
)
print ' ok'
ELSE 
print ' not ok'

Ответы [ 6 ]

4 голосов
/ 07 апреля 2011
SELECT CASE WHEN COUNT(c) =1 THEN 'Y' ELSE 'N' END
FROM 
(VALUES (CAST(@string AS SQL_VARIANT)),(@float),(@bit),(@int)) T (c) 
2 голосов
/ 08 апреля 2011

Мне кажется, я знаю тип создаваемой вами установки.В этих обстоятельствах я обычно определяю свои структуры данных как:

CREATE TABLE DataItems (
    DataItemID int IDENTITY(1,1) not null,
    Name varchar(10) not null,
    TypeRequired varchar(6) not null,
    constraint PK_DataItems PRIMARY KEY (DataItemID),
    constraint CK_TypeRequired CHECK (TypeRequired in ('STRING','FLOAT','BIT','INT'),
    constraint UQ_DataItems_TypeCheck UNIQUE (DataItemID,TypeRequired)
)

Обратите внимание, что я сделал DataItemID,TypeRequired суперключем, поэтому я могу ссылаться на него в ограничении внешнего ключа.

Теперь в таблице, которая собирает данные:

CREATE TABLE Answers (
    AnswerID int IDENTITY(1,1) not null,
    /* Other columns to FK to e.g. Client, Users, Session, whatever */
    DataItemID int not null,
    Type varchar(6) not null,
    StringValue varchar(max) null,
    FloatValue float null,
    BitValue bit null,
    IntValue int null,
    constraint PK_Answers PRIMARY KEY (AnswerID),
    constraint FK_Answers_DataItems FOREIGN KEY (DataItemID) references DataItems (DataItemID),
    constraint FK_Answers_DataItems_TypeCheck FOREIGN KEY (DataItemID,Type) references DataItems (DataItemID,TypeRequired),
    constraint CK_Answers_TypeCheck CHECK (
        (FloatValue is null or TypeRequired = 'FLOAT') and
        (StringValue is null or TypeRequired = 'STRING') and
        (BitValue is null or TypeRequired = 'BIT') and
        (IntValue is null or TypeRequired = 'INT')),
    constraint CK_Answers_NotNUll CHECK (
        FloatValue is not null or StringValue is not null or BitValue is not null or IntValue is not null)
)

Второе ограничение внешнего ключа гарантирует, что столбец типа соответствует определенному типу для элемента данных, а пара проверочных ограничений гарантирует, что ровно один столбец (иправый столбец) не равен нулю.

Если вам нужно скрыть столбец Тип от пользователей, я бы предложил переименовать приведенную выше таблицу (например, _Answers), создав представление с триггером вставки:

CREATE VIEW Answers
WITH SCHEMABINDING
AS
     SELECT
         AnswerID,
         DataItemID,
         StringValue,
         FloatValue,
         BitValue,
         IntValue
     FROM
         dbo._Answers

CREATE TRIGGER T_Answers_I
ON Answers
INSTEAD OF INSERT
AS
     INSERT INTO _Answers (DataItemID,Type,StringValue,FloatValue,BitValue,IntValue)
     SELECT i.DataItemID,di.Type,i.StringValue,i.FloatValue,i.BitValue,i.IntValue
     FROM inserted i inner join DataItems di on i.DataItemID = di.DataItemID
2 голосов
/ 07 апреля 2011

Я не обязательно уверен, что он более читабелен (хотя, думаю, если бы вы абстрагировали его от функции, это могло бы быть), но

if((case when @string is null then 0 else 1 end + 
    case when @float  is null then 0 else 1 end +
    case when @bit    is null then 0 else 1 end + 
    case when @int    is null then 0 else 1 end) = 1) 
  .... 

немного гибче?

1 голос
/ 10 февраля 2012

Разве мы не можем проверить это:

IF ISNULL(@string, '') <> ''
    OR ISNULL(@float, 0) <> 0
    OR @bit IS NOT NULL
    OR ISNULL(@int, 0) <> 0
   PRINT 'There is atleast one value'
ELSE
   PRINT 'ALL ARE NULL'
1 голос
/ 07 апреля 2011

Я думал, что был умен, но это сработает только в том случае, если ни одна из 4 переменных не может иметь одинаковое значение, поэтому во многих случаях это бесполезно.В любом случае я опубликую это:

IF(   
(COALESCE(@string,@float, @bit, @int) IS NULL)     
OR (COALESCE(@string, @float, @bit, @int) != COALESCE(@int, @bit, @float, @string))
) 
print 'not ok' 
ELSE  print 'ok'

COALESCE изменяется слева направо, пока не достигнет значения NOT NULL, поэтому, если вы инвертируете порядок, вы получите другой результат, если у вас есть более одной переменной,набор (если только переменные не могут иметь одно и то же значение одновременно)

1 голос
/ 07 апреля 2011

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

DECLARE @string varchar
DECLARE @float float
DECLARE @bit bit
DECLARE @int int

SET @string=NULL  -- Exactly one of these variables needs to be set
SET @float=NULL  --
SET @bit=NULL    -- 
SET @int=NULL     --


if  ((case when @string is null then 1 else 0 end) 
        ^ (case when @float is null then 1 else 0 end)
        ^ (case when @bit is null then 1 else 0 end)
        ^ (case when @int is null then 1 else 0 end)) = 1
print 'ok'
else
print 'not ok'

Комментарии? Критицизмы? Также не уверен, насколько эффективны операторы CASE.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...