如何在SELECT语句中将NULL值设置为声明的变量?

时间:2016-07-27 19:25:49

标签: sql-server tsql set

我不明白发生了什么。

DECLARE @X DATETIME;

--FIRST
SELECT @X = ContractDate
FROM dbo.Foo C
WHERE ProjectCode = 120125 
  AND VersionID = (SELECT MIN(VersionID) 
                   FROM dbo.Foo 
                   WHERE ProjectCode = C.ProjectCode)

SELECT @X

--SECOND
SELECT @X = ContractDate
FROM dbo.Foo C
WHERE ProjectCode = 130192 
  AND VersionID = (SELECT MIN(VersionID) 
                   FROM dbo.Foo 
                   WHERE ProjectCode = C.ProjectCode)

SELECT @X

首先'SELECT @X'必须返回'2012-09-10',Second'SELECT @X'必须返回'NULL'但Second返回'2012-09-10'。

第一个问题:为什么第二个'SELECT @X'没有返回NULL?

上面的代码块在WHILE语句中。原始代码在这里:

DECLARE @ProjectCode NVARCHAR(10)

DECLARE CRS CURSOR FOR
    SELECT P.ProjectCode
    FROM dbo.Foo P

OPEN CRS

FETCH NEXT FROM CRS INTO @ProjectCode

WHILE @@FETCH_STATUS = 0
BEGIN
    DECLARE @ProjectContractDate_T0 DATETIME;

    SELECT @ProjectContractDate_T0 = ContractDate
    FROM dbo.FooFoo C
    WHERE ProjectCode = @ProjectCode 
      AND VersionID = (SELECT MIN(VersionID) 
                       FROM dbo.FooFoo 
                       WHERE ProjectCode = C.ProjectCode)

    SELECT @ProjectContractDate_T0

    FETCH NEXT FROM CRS INTO @ProjectCode
END

CLOSE CRS
DEALLOCATE CRS

while循环处理:

  • ProjectCode = 120125时,@ProjectContractDate_T0为'2012-09-10',

然后下一个

  • Projectcode = 130192@ProjectContractDate_T0必须为NULL,但为“2012-09-10”。

怎么可能?

在while循环中,为什​​么ProjectContractDate_T0没有重置为NULL?

2 个答案:

答案 0 :(得分:3)

当select的where子句过滤掉每个记录时,不会进行任何分配。因此,@ x具有上次分配的值。

请尝试这种方式:

SELECT @X = (SELECT ContractDate
             FROM dbo.Foo C
             WHERE ProjectCode = 130192 
             AND VersionID = (SELECT MIN(VersionID) 
                              FROM dbo.Foo 
                              WHERE ProjectCode = C.ProjectCode))

这样,您的分配"选择"不包含任何where子句,因此它将始终执行。如果内部选择没有结果,则为X分配null。

这是另一个例子

DECLARE @x NVARCHAR(25) = 'Initial value'

SELECT @x = 'Value1'
WHERE 0 = 1

--@x is still "Initial value" cause 0 isn't egal to 1

SELECT @x = (SELECT 'Value2'
             WHERE 0 = 1)

--@x is now null because the impossible where clause didn't hindered the assignation. It has just made the inner select to return null instead.

正如马丁建议的那样,您可以使用

重置之前的值
SELECT @X = null

但我更喜欢只选择一个赋值,因为它更短,而且它只是SQL引擎的一条指令行。

旁注:

您的"选择"可以用"#by顺序重写。和" TOP 1"条款得到最低限度。它给出了相同的结果,写入/读取的时间更短。

SELECT @ProjectContractDate_T0 = (SELECT TOP 1 ContractDate
                                  FROM dbo.FooFoo C
                                  WHERE ProjectCode = @ProjectCode 
                                  ORDER BY VersionID)

它做同样的事情。

答案 1 :(得分:2)

在select语句中设置变量时,如果select语句没有返回任何行,则不会有赋值;你的变量的值不会改变。

关于循环问题,T-SQL中变量的范围是批处理,而不是循环。每次迭代都不会重新声明或初始化。

因此,在T-SQL中,如果您希望在其他语言中重新初始化,则将变量显式初始化为null是一种很好的做法。这可能会使其更具可读性并更好地显示您的意图。

另外,我应该补充说,当基于集合的解决方案(查询)成为可能时,通常最好避免使用T-SQL中的游标和循环,这几乎总是如此。显然这取决于你的使用案例,但没有说明。