Sql server 检查是否只有一个变量不为空

Sql server 检查是否只有一个变量不为空,sql-server,tsql,Sql Server,Tsql,在SQLServer2008数据库中的触发器中,我需要检查是否有一个变量不为null。这段代码可以满足我的需要,但它可以用更少的行数和更可读的代码来完成吗 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

在SQLServer2008数据库中的触发器中,我需要检查是否有一个变量不为null。这段代码可以满足我的需要,但它可以用更少的行数和更可读的代码来完成吗

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'

我找到了一个替代的解决方案,但它不是更少的行。它使用按位异或运算符。我不确定我是否喜欢它-但这确实意味着每个变量只检查一次,而不是在每行上检查一次,因此它可能满足您的可读性要求:

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
语句的效率有多高。

我不一定确定它是否更可读(尽管我想如果您将它抽象为一个函数,它可能会更可读),但是


是不是更灵活一点

我认为我很聪明,但这只有在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'

从左到右合并evals,直到它达到一个非空值,因此如果你颠倒顺序,如果你有多个变量集,你将得到不同的结果(除非变量可以同时具有相同的值)

我想我知道你正在创建的设置类型。在这些情况下,我通常将我的数据结构定义为:

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)
)
第二个外键约束确保类型列与为数据项定义的类型匹配,而检查约束对确保恰好有一列(以及右列)不为null

如果您需要对用户隐藏Type列,那么我建议重命名上表(例如,
\u 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

我们不能用这个检查一下吗:

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'
如果为空(@string,)“”
或ISNULL(@float,0)0
或者@bit不为空
或ISNULL(@int,0)0
打印“至少有一个值”
其他的
打印“全部为空”

对此我有一种不好的感觉-只想检查一下您是否意识到该语句更新的所有行都会触发触发器。因此,如果您有类似于
的内容,请选择@string=string、@float=float。。。从inserted
,当涉及到多行插入/更新/删除时,触发器已被破坏。此外,如果这是为了确保基础表的完整性(这是唯一需要的检查),最好将最终得到的任何谓词放在检查约束中,而不是触发器中。谢谢,我忘了有多行。我得调查一下。我需要触发器的原因是,我需要对照另一个表检查约束,该表确定允许设置的字段。也许我会把“只有一个”的条件放入一个CHECK约束中,并对照触发器中的表进行检查。我喜欢这个条件的简洁性。这里还有一个额外的挑战:是否可以对其进行修改,使其进入检查约束?我会将此标记为问题的答案。虽然Damien的答案解决了我的问题,但我相信这是对原始问题的最短答案。非常感谢。这是我问题的解决办法,尽管这不是问题的答案关于这一点,我唯一觉得有点难看的是,保存类型列两次,并将允许类型的字符串放在代码中的两个位置。如果字符串值不能转换为数字,此查询也会遇到问题。
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
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'