web-dev-qa-db-ja.com

PostgreSQLのcitextタイプは、LOWER()を使用して行われる文字列比較にどのように影響しますか?

私の質問のきっかけは、一致する文字列がlower()の1つ以上のインスタンスにラップされているかどうかに関係なく、citext列から選択するときにPostgreSQLが一貫して動作することを望んでいたことです。 (そのようなラッピングは私のコントロールを超えています)。そうではないようです。 (もちろん、私のテストが無効であるか、基本的な概念を誤解している可能性は十分にあります。)

テストシナリオを再現する手順

CREATE EXTENSION IF NOT EXISTS citext;
CREATE TABLE users (id int, email citext);
INSERT INTO users(id, email) VALUES
  (1, '[email protected]');

テスト

citextタイプを使用すると予想されるように、小文字のバリアントは次の結果をもたらします。

# select * from users where email = '[email protected]';
 id |      email
----+------------------
  1 | [email protected]
(1 row)

=演算子をlikeに変更すると、次の結果が得られます。

select * from users where email like lower('[email protected]');
 id |      email
----+------------------
  1 | [email protected]
(1 row)

「逆」と同じように:

# select * from users where lower(email) = '[email protected]';
 id |      email
----+------------------
  1 | [email protected]
(1 row)

lower()で両方の値をラップするのと同じように:

# select * from users where lower(email) = lower('[email protected]');
 id |      email
----+------------------
  1 | [email protected]
(1 row)

私の質問

では、なぜ次のクエリはこのインスタンスで結果を返さないのですか?

# select * from users where email = lower('[email protected]');
 id | email
----+-------
(0 rows)

citextタイプのマニュアルによると

基本的に、値を比較するときに内部的にlowerを呼び出します。

有効な言葉は「本質的に」のようです。このステートメントは、次のことを意味しますが、結果は得られます。

select * from users where lower(email) = lower(lower('[email protected]'));
 id |      email
----+------------------
  1 | [email protected]
(1 row)

これは、上記のドキュメントのLimitationsセクションにある次の警告に関連している可能性がありますか?

citextの大文字小文字の区別の動作は、データベースのLC_CTYPE設定によって異なります。

# SHOW LC_CTYPE;
  lc_ctype
-------------
 en_US.UTF-8
(1 row)

この点に関する説明は大歓迎です。

1
Ben Johnson

tldr;大文字と小文字を区別しないものと大文字と小文字を区別するものを比較する場合は、明示的にする必要があります。textは明示的に大文字と小文字を区別し、citextは明示的に大文字と小文字を区別しません。あなたshould両側にキャストを提供し、明示的に

lower()に関するいくつかのこと

  • lower()が入力されます
  • 引数がtextの場合、常にtextを返します。

他のいくつかのポイント

  • リテラルと比較する場合、型は不明です(内部的には明示的にunknownです)。
  • 演算子は関数です。
  • 関数は、実行時にPostgreSQLの型を強制します。

この場合のタイプは次のとおりです。

_-- text = unknown
-- unknown promoted to text, this has nothing to do with citext
lower(email) = '[email protected]';

-- text = text
-- this has nothing to do with citext
lower(email) = lower('[email protected]');

-- text = text
-- this has nothing to do with citext    
lower(email) = lower(lower('[email protected]'));

-- citext LIKE text
-- LIKE is smart `operator ~~(citext,text)` via `texticlike`
-- WORKS
email like lower('[email protected]');

-- citext = unknown
-- unknown promoted to citext, there is an `operator =(citext,citext)`
-- WORKS
email = '[email protected]';

-- citext = text
-- citext promoted to text, there is no `operator =(citext,text)`
-- FAILS
email = lower('[email protected]');
_

要約すると、operator =(citext,citext)があります。だからあなたはできる

_email = lower('[email protected]')::citext;
_

必要に応じて、または_=_を大文字と小文字を区別するルートではなく大文字と小文字を区別しないルートに設定する独自の演算子を定義できます。でもそれは恐ろしい練習だと思いますが、私はいつもキャストします。

1
Evan Carroll