きなこもち.net

.NET Framework × UiPath,Orchestrator × Azure × AWS × Angularなどの忘備録

EF Core 3.1 × Postgres × 実際に出力されるSQLの検証を続けてみた

この記事の目的

この記事は、
EF Coreが出力する実際のSQLの検証を行うこと
を目的としています。
今回は、Selectに焦点を絞って、いろいろな条件でクエリを投げた結果をまとめていきます。
前回は、クエリ式、メソッド式どちらもやりましたが、今回からしばらくは、クエリ式中心でまとめていきます。
実行環境などは、前回のブログと同じものを利用しています。

変更点

AccountテーブルにNote列を追加しました。
f:id:kinakomotitti:20200607215550p:plain

関連ブログ

www.kinakomotitti.net


本題

WHERE句 Null検索

NotNull制約が付いたEmail列と、制約が付いていないNote列に対して、以下のようなクエリを投げてみます。

(from account in context.Account
 where account.Email != null &&
            account.Email != string.Empty &&
            account.Note != null &&
            account.Note != string.Empty
 select account).ToList();


結果・・・

SELECT a.user_id, a.created_on, a.email, a.last_login, a.note, a.password, a.sex_id, a.username
FROM account AS a
WHERE((a.email <> '') AND
              (a.note IS NOT NULL)) AND(a.note <> '')
  • 1つの区切りごとに括弧がつけられるので、どこまでが一つの条件かわかりやすくなっています。条件が3つ(A、B、C)ある場合、 (A and B) and C のような形式で付与されているようでしす。条件が4つ(A、B、C、D)ある場合、 ((A and B) and C ) and D になるイメージですね。こうしている理由は何だろう・・・こうしたほうがパフォーマンスがあがるのかなぁ。
  • Email、Noteどちらの列にも、Nullに関するチェック処理を追加しましたが、Emailのほうの条件だけ無視されています。
  • から文字列でないことの確認は、どちらの列も同じように出力されました。

Null が無視される件については、DBContextクラスのRequire設定が影響を与えていました。自動生成されたDBContextクラスの設定を以下のように変更することで、Null条件が無視されずに出力されることを確認しました。※無理やり出す必要もないですね(´▽`)

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Account>(entity =>
            {
                entity.Property(e => e.Email)
                    //.IsRequired()   <- Here
                    .HasColumnName("email")
                    .HasMaxLength(355);
                entity.Property(e => e.Note)
                    .HasColumnName("note")
                    .HasColumnType("character varying");
            });
        }
SELECT a.user_id, a.created_on, a.email, a.last_login, a.note, a.password, a.sex_id, a.username
FROM account AS a
WHERE (((a.email IS NOT NULL) AND (a.email <> '')) AND 
            (a.note IS NOT NULL)) AND (a.note <> '')

WHERE句 Null検索 with String メソッド

文字列の値が、Nullであること、空文字列であることを確認するときにお世話になるメソッド・・・そう、String.IsNullOrEmpty。これを使って、同じようにEmailとNote列の比較を行ったらどうなるか、調べてみました。

//IsNullOrEmptyを使った場合
SELECT a.user_id, a.created_on, a.email, a.last_login, a.note, a.password, a.sex_id, a.username
FROM account AS a
WHERE a.email = ''

//IsNullOrEmptyを使った場合
SELECT a.user_id, a.created_on, a.email, a.last_login, a.note, a.password, a.sex_id, a.username
FROM account AS a
WHERE (a.note IS NULL) OR (a.note = '')


//IsNullOrWhiteSpaceを使った場合
SELECT a.user_id, a.created_on, a.email, a.last_login, a.note, a.password, a.sex_id, a.username
FROM account AS a
WHERE BTRIM(a.email, E' \t\n\r') = ''

//IsNullOrWhiteSpaceを使った場合
SELECT a.user_id, a.created_on, a.email, a.last_login, a.note, a.password, a.sex_id, a.username
FROM account AS a
WHERE (a.note IS NULL) OR (BTRIM(a.note, E' \t\n\r') = '')
  • Stringクラスのメソッドを使った場合も、NotNull制約がある列に対しては、Nullの検査が無視されました。
  • String.IsNullOrWhiteSpaceを使った場合、文字列から、特定の文字列を削除した後の結果に対して、空文字検索が行われました。特定の文字列は、半角スペース、タブ、改行、キャリッジリターンの4つが含まれています。全角スペースは、含まれていません。ちなみに、以下のように、C#でのString.IsNullOrWhiteSpaceでは、全角スペースも含まれています。ここは、気を付けないといけないポイントですね!
string.IsNullOrWhiteSpace(null)
// →true
string.IsNullOrWhiteSpace(string.Empty)
// →true
string.IsNullOrWhiteSpace(" ")
// →true
string.IsNullOrWhiteSpace(" ")
// →true
string.IsNullOrWhiteSpace("あ")
// →false

まとめ

EF Coreのクエリ条件で、string.IsNullOrWhiteSpaceを使うと、半角スペース、タブ、改行、キャリッジリターンを考慮して判定をするSQLが実行される。ただし、C#の仕様と違い、全角スペースは含まれない。


つづく!