今日、私は職場で何かをテストしていて、これに出くわしました
ケース1:
Declare @a nvarchar(20)
Set @a = null
Select IsNull(LTrim(RTrim(Lower(@a))), -1)
ケース2:
Select IsNull(LTrim(RTrim(Lower(null))), -1)
ケース1の結果はです-1
が*
、ケース2の場合、どちらの場合も同じ結果を期待していました。何らかの理由?
今日、私は職場で何かをテストしていて、これに出くわしました
ケース1:
Declare @a nvarchar(20)
Set @a = null
Select IsNull(LTrim(RTrim(Lower(@a))), -1)
ケース2:
Select IsNull(LTrim(RTrim(Lower(null))), -1)
ケース1の結果はです-1
が*
、ケース2の場合、どちらの場合も同じ結果を期待していました。何らかの理由?
データ型の宣言がない場合、この場合のnullはvarchar(1)として宣言されます。これは、結果を#tempテーブルに選択することで確認できます。
Select IsNull(LTrim(RTrim(Lower(null))), -1) as x INTO #x;
EXEC tempdb..sp_help '#x';
結果の中には次のものがあります。
Column_name Type Length
----------- ------- ------
x varchar 1
-1はvarchar(1)に収まらないため、出力として*を取得しています。これは次のようになります。
SELECT CONVERT(VARCHAR(1), -1);
文字列に折りたたむ場合は、整数を一重引用符で囲むことをお勧めします。これにより、意図しない整数<->文字列変換によって混乱が生じることはありません。
SELECT CONVERT(VARCHAR(1), '-1'); -- yields "-"
SELECT CONVERT(VARCHAR(30), '-1'); -- yields "-1"
SQL Serverが明示的に提供された「値」をどのように処理するかについてはnull
、特に複雑な式によってデータ型の優先順位よりも優先される可能性のある評価ルールを予測することが難しい場合は、想定しません。
SQL Serverには、「型付きNULL」と「型なしNULL」があります。
最初のケースでは、NULLが型指定されます。つまり、NULLがaであることが認識されているvarchar(20)
ため、関数が内部値をラップすると、そのデータ型が式全体に伝播されます。
2番目のケースでは、NULLは型指定されていないため、周囲の式からNULLの型を推測する必要があります。このIsNull
関数は、最初のオペランドのデータ型を評価し、それを式全体に適用するため、NULLのデフォルトは次のようになりますvarchar(1)
。
PRINT sql_variant_property(IsNull(LTrim(NULL), -1), 'BaseType'); -- varchar
PRINT sql_variant_property(IsNull(LTrim(NULL), -1), 'MaxLength'); -- 1
もう1つの厄介な問題は、IsNull
型の昇格が同じように行われないことですCoalesce
(ただし、Coalesceは関数ではないために独自の問題があります。これは、CASE式に拡張され、式の繰り返し評価によって予期しない副作用が発生する場合があります)。見て:
SELECT Coalesce(LTrim(NULL), -1);
これにより、-1
データ型がint
!になります。
SQL Serverのデータ型の優先順位を確認すると、それが。int
よりもはるかに高いことがわかります。varchar
したがって、式全体が。になりint
ます。
裸のNULLは、文字を期待するLOWER()に渡されています。これはデフォルトで1文字幅になっています。値「-1」はこのフィールドに収まらないため、「*」を返します。
次の方法でも同じ効果が得られます。
select isnull(CAST(NULL as varchar(1)), -1)
次のコードも問題を引き起こします。
declare @val varchar;
set @val = -1
select @val
COALESCE()はこの問題を引き起こさないことに注意してください。
これは完全に文書化された動作であると確信しています。