2016-07-27 5 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日。

最初の質問:なぜ2番目の '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 

ループであるが処理:

  • ProjectCode = 120125は、@ProjectContractDate_T0が '2012年9月10日' である、

次いで次

  • Projectcode = 130192@ProjectContractDate_T0はNULLにする必要がありますが、 '2012-09-10'です。

どのようにすることができますか?

whileループでは、なぜProjectContractDate_T0がNULLにリセットされませんでしたか?

+0

私はこれをデータ関連として表示します。関連するデータについては何も考えていません。 – TheGameiswar

+1

これはリレーショナルデータベースに対する古典的な手続き型アプローチであり、悪いコードを示すようには建設的ではありません。それがなければならない:dbo.Foo P LEFT OUTERは( C.ProjectCode = P.ProjectCode AND C.VersionID =( MINを選択ON dbo.FooFoo C をJOIN FROM SELECT P.ProjectCode、C.ContractDate dbo.FooFoo C_Min FROM(VERSIONID) WHERE C_Min.ProjectCode = P.ProjectCode ) –

答えて

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句、これは常に実行されますが含まれていません。内側の選択は何も得られない場合は、nullを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 

しかし、私はそれがより短いので、1つの割り当て選択を持つことを好みます。これは、SQLエンジンの命令ラインが1つ少なくなるためです。


サイドノート:

あなたの "選択" 最小を取得すると、 "TOP 1" "ORDER BY" 節で書き換えることができます。これは同じ結果をもたらし、書き込み/読み込みが短くなります。

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

同じことをします。

+3

うん、または選択秒前にnullにちょうどreinitilaise '@のX '。 –

+0

どうもありがとう。私はあなたの提供のように分かった。I書いた:SET @ X =(SELECT ContractDate FROM dbo.Foo C どこProjectCode = 130192 AND VersionID =(SELECT MIN(VersionID) FROM dbo.Foo どこProjectCode = C.ProjectCode))ありがとうAXMIM、マーティン – hebset

+0

AXMIM、あなたのサイドノートは非常に便利です、ありがとう。 – hebset

2

selectステートメントで変数を設定する場合、そのselectステートメントが行を返さない場合、割り当てはありません。変数の値は変更されません。

ループの問題に関して、T-SQLの変数の範囲はループではなくバッチです。反復ごとに再宣言されたり、初期化されたりすることはありません。

このように、別の言語で再初期化が必要な場合は、変数を明示的にnullに初期化することをT-SQLで行うことをお勧めします。これにより、より読みやすくなり、あなたの意図をよりよく示すことができます。

また、T-SQLのカーソルとループは、ほとんどの場合、セットベースのソリューション(クエリ)が可能な場合には避けるべきであるということを付け加えておきます。明らかに、これはあなたのユースケースに依存しますが、それは述べられていません。

+0

'ループの問題に関して、T-SQLの変数の範囲はループではなくバッチです。反復ごとに再宣言されたり初期化されたりしません。私もそう思う。ありがとうTim。 – hebset

関連する問題