Postgres列挙型

Postgresは列挙型の概念をサポートします

急いで、私はそれがデータベースとクライアント一般にとって何であるかを理解しようとしました:

  1. enum-静的な順序付けられた値のセット
  2. 列挙値はディスク上で4バイトかかります
  3. 登録事項、つまり「ハッピー」と「ハッピー」は同じものではありません
  4. 異なる列挙型を相互に比較することは不可能です(共通の型またはファイル演算子を使用する場合は可能です)
  5. 列挙自体にない列挙型の列に値を入れることは不可能です

OK、すべてがPostgresでのみ、いつものように見える

目で見やすいように、ステータスがテキスト形式で保存されているテーブルがいくつかあります
興味深いことに、これらのテーブルの1つを完全に空にし、そのコピーを作成しましたが、ステータス列を対応する列挙型に置き換えました。



違いはあまり目立たないので、テストデータはあまりありません。



そして、これはわずかに大量のデータの例ですが、データもテストします



いずれにせよ、おおよそ1ギガバイトの節約になりますが、実際には数ギガバイトです(2ギガバイトですが、もちろんそれ以上)。

バックアップが毎日行われ、90日間保存されるとします。

Enumは180ギガの余分なデータを削除しますが、数バイトでのマイクロ最適化にはそれほど悪くありません。
そして、このプレートにはすでに9種類の転送があります(サイズをまだ評価していません)

サンプル自体に違いはありません(ステータス列は列挙型になりました)

select date, contragentname, amount, currency, status from transactions where companyid = '208080cd-7426-430a-a5c8-a83f019da923' limit 10; select date, contragentname, amount, currency, status from transactions_enum where companyid = '208080cd-7426-430a-a5c8-a83f019da923' limit 10; 



クエリの実行に関して幅に注意してください

ただし、コードの読み取りはまったく変更する必要はありません(BLToolkit + Npgsql)。

ただし、コードのみに依存します。たとえば、.NETバックエンドと対応する列挙型があり、マップデータはBLToolkitであるため、列挙型フィールドの場所にリクエストを送信する場合は、次のように置き換えます。

 (CASE currency WHEN 'NAN' THEN 0 WHEN 'RUR' THEN 1 WHEN 'USD' THEN 2 WHEN 'EUR' THEN 3 WHEN 'CNY' THEN 4 ELSE 0 END) as currency 


したがって、読み取りに問題はありません。 そして、以下の記録的なトラブルがあります:

 error: column status is of type enum_transaction_status but expression is of type text 

なぜなら 要求は次のように形成されます。

 update transactions_enum set status = $1::text where id = $2 

理解できない人のために、タイプのテキストが明示的に示されています。

これは簡単に回避できます。

 CREATE FUNCTION enum_transaction_status_from_str (text) returns enum_transaction_status AS 'select $1::varchar::enum_transaction_status' --    varchar,     LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT; --      CREATE CAST (text AS enum_transaction_status) WITH FUNCTION enum_transaction_status_from_str(text) AS ASSIGNMENT; 

ケースを書くために....それから...まあまあのアイデアですが、夏に簡単な読書をすることができなかったので、BLToolkitが良くないと判断してDapperを試しました。

そして、彼が読み書きをしたいという魔法と松葉杖なしで、彼は要求で示しました

 using (var conn = new NpgsqlConnection(connString)) { conn.Open(); Dapper.SqlMapper.Execute(conn, "update transactions_enum set status = :status where id = :id", new { id, status = ETransactionStatus.Executed.ToString() } ); var tran = Dapper.SqlMapper.QueryFirst<TransactionInStorageFull>(conn, "select id, status from transactions_enum where id = :id", new { id } ); Console.WriteLine(tran.Id + " : " + tran.Status.ToString()); Dapper.SqlMapper.Execute(conn, "update transactions_enum set status = :status where id = :id", new { id, status = ETransactionStatus.Deleted.ToString() } ); tran = Dapper.SqlMapper.QueryFirst<TransactionInStorageFull>(conn, "select id, status from transactions_enum where id = :id", new { id } ); Console.WriteLine(tran.Id + " : " + tran.Status.ToString()); } 



列挙型がクールであることはすでに明らかなので、列挙型をどのように使用するを提案します

  1. 作成

     CREATE TYPE e_contact_method AS ENUM ( 'Email', 'Sms', 'Phone') 
  2. テーブル使用

     CREATE TABLE contact_method_info ( contact_name text, contact_method e_contact_method, value text) 
  3. 挿入、更新、比較するとき、文字列を列挙にキャストする必要はありません。文字列が列挙に含まれていれば十分です(そうでない場合、エラーはenumの無効な入力値であり、大きなプラス、IMHOです)

     INSERT INTO contact_method_info VALUES ('Jeff', 'Email', 'jeff@mail.com') 
  4. すべての可能な値を表示

     select t.typname, e.enumlabel from pg_type t, pg_enum e where t.oid = e.enumtypid and typname = 'e_contact_method'; 
  5. 新しい値を追加する

     ALTER TYPE e_contact_method ADD VALUE 'Facebook' AFTER 'Phone'; 
  6. 既存のテーブルで列挙する行を変更する

     ALTER TABLE transactions_enum ALTER COLUMN status TYPE enum_transaction_status USING status::text::enum_transaction_status; 

一部の人にとっては、データベースレベルで追加の転送を導入することは不必要に複雑に思えるかもしれませんが、データベースは常にサードパーティサービスとして扱われるべきです。 それから、異常なことは何もありません-サードパーティのサービスに何らかの定義があります。便宜上、同じものを背面で取得する必要があり、フロントエンドもこれらの転送の一部を複製します

すべてが正常であり、プラスのみであり、 migrationsで新しい値を追加できます。

いくつかのリンク:

Source: https://habr.com/ru/post/J353556/


All Articles