2016-07-27 112 views
0

我不明白发生了什么事情。如何在SELECT语句中将声明变量的NULL值设置为?

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年9月10日',Second'SELECT @X”必须返回 'NULL',但第二个返回 '2012年9月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年9月10日',

那么接下来

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

这怎么可能?

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

+0

你需要打印出所有的值和调试,我把它看作是数据相关的,我们对涉及的数据没有任何想法 – TheGameiswar

+1

这是一个经典的关于数据库的“过程式”方法,并不像说明错误的代码那样具有建设性。它应该是: SELECT P.ProjectCode,C.ContractDate FROM dbo.Foo P LEFT OUTER JOIN dbo.FooFooÇ ON( C.ProjectCode = P.ProjectCode AND C.VersionID =( SELECT MIN (VERSIONID) FROM dbo.FooFoo变量C_min WHERE C_Min.ProjectCode = P.ProjectCode ) –

回答

3

当选择的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.

下面是另一个例子

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

SELECT @x = 'Value1' 
WHERE 0 = 1 

[email protected] is still "Initial value" cause 0 isn't egal to 1 

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

[email protected] 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引擎的少一个指令行。


旁注:

你 “选择” 可以用一个和 “TOP 1” 条款 “按订单”,以获得最低改写。它给出了相同的结果,并且写入/读取的时间更短。

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

它做同样的事情。

+3

是的,或只是reinitilaise'@ X'到零之前第二选择。 –

+0

非常感谢。我发现喜欢你的报价。我写道:SET @X =(SELECT ContractDate FROM dbo.Foo C WHERE ProjectCode = 130192 AND VersionID =(SELECT MIN(VersionID) FROM dbo.Foo WHERE ProjectCode = C.ProjectCode))谢谢AXMIM,马丁 – hebset

+0

AXMIM,您的侧面说明非常有用,非常感谢。 – hebset

2

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

至于你的循环问题,T-SQL中变量的作用域是批处理,而不是循环。每次迭代都不重新声明或初始化。

因此,在其他情况下可能需要重新初始化时,在T-SQL中显式初始化变量为null可能是一个好习惯。这可能会使其更具可读性并更好地表达您的意图。

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

+0

'至于你的循环问题,T-SQL中变量的范围是批处理,而不是循环。它不会在每次迭代时重新声明或初始化。'我也是这么想。谢谢蒂姆。 – hebset