【備忘録】char型のカラムが空でも判定結果としては空文字と同等になるという話【SQLSERVER】

眠気が家出してしまったので、メモっておこうと思ったことを書く。※執筆中現時刻、朝5時

仕事でSQLを書いてきた際に、ぼーっとしていて以下のような判定を作ってしまった。

select * from TABLE1
where COL2 <> '' -- 空でないレコードを取得する

COL2はchar型だったため、実際には固定長でスペースが存在し、正しく取得されない判定となっているハズだった。

しかし、実際には正しく取得されていることに後から気づいたのだ。

「スペースと判定しちゃうから絶対に”空でない”という判定結果になるはずでは???」と思ったため、少し調べてみた。

実際にテスト用のレコードで挙動を確かめてみる。

-- テスト用のテーブル作成
create table SPACE_TEST_TABLE(
    col_char		char(2) null, -- char型のカラムを作成
    col_varchar		varchar(2) null) -- varchar型のカラムを作成

-- 空文字のレコードを追加
insert SPACE_TEST_TABLE values('','') 

-- 取得
select 
    -- char型を確認する
    col_char -- 普通に取得
    ,len(col_char) as char_LEN -- 文字の長さを取得
    ,datalength(col_char) as char_DATALENGTH -- データの長さを取得
    ,case when col_char = '' then 'BLANK' else 'SPACE' end as char_STATUS -- 空文字との比較結果を表示
    -- varchar型を確認する
    ,col_varchar -- 普通に取得
    ,len(col_varchar) as varchar_LEN -- 文字の長さを取得
    ,datalength(col_varchar) as varchar_DATALENGTH -- データの長さを取得
    ,case when col_varchar = '' then 'BLANK' else 'SPACE' end as varchar_STATUS -- 空文字との比較結果を表示
from SPACE_TEST_TABLE

結果はこうなる

どういうことかと言うと。

・len関数は、文字の長さを取得するため、char型でも余分なスペースは切り落としてbyte数を取得する
 ⇒頭に入れておかないとどっかでバグを起こしそう
・datalength関数は、データの長さを取得するため、char型の場合はスペース分込みのbyte数を取得する
 ⇒わかる
・空文字と中身がスペースだけのchar型を比較すると、結果はTRUEとなる
 ⇒は?
・空文字と中身が空文字のvarchar型を比較すると、結果はTRUEとなる
 ⇒わかる

という結果である。

調べたところ、以下のドキュメントに辿り着いた。

https://support.microsoft.com/en-us/topic/inf-how-sql-server-compares-strings-with-trailing-spaces-b62b1a2d-27d3-4260-216d-a605719003b0

ついでにこちらも参考にした。

https://sql55.com/column/string-comparison.php

どうやら、SQLSERVERでは文字列と文字列を比較する際、短い方に空白を追加してbyte数を合わせてから比較を行う仕様らしい。

知ってしまえばどうということは無いかもしれないが、性能にも影響が出そうな仕様だなと感じた。
例えば最初のケースのような、where句で””かどうかをchar型を対象に判定するような記載をした場合、検索対象のレコードすべてに対して比較の都度、空文字を追加する処理を行っているのではないか?

備忘録として書き留めておく。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です