PostgreSQLでCSVファイルをレコードに取り込む(COPY編)
COPY 句
ファイル ⇔ テーブル間のデータを高速にコピーする際に使用する。
ファイルの内容を無条件でインポートする際は高速で楽なのだが、あまり複雑なことができず、以下のケースで困る。
- 任意の条件によるインポート対象の絞り込み
- キー重複
COPYコマンドを使用する場合は、上述のようなファイル・データが扱われないよう調整するのが最初の手になる。
1. については、テンポラリテーブルを作成 → INSERT SELECT コマンドの発行(+テンポラリテーブルの破棄)
2.も同時に対応する場合はON CONFLICTコマンドを追加してやればよい。
2. のみの場合について、
レプリケーション環境でなければpg_bulkloadを使えばよい。
DBなので微妙なライン。。
レプリケーション環境である場合は、file_fdwを使用して外部テーブル化し、INSERT INTO SELECT で ON CONFLICTコマンドを発行する。
- CSVファイルで受け取ったデータを、一括でテーブルに登録する
- テーブルから全レコードのデータをファイル出力する
といったユースケースにおいて、プログラム+INSERT、SELECT句を使用するよりも簡単かつ高速に実現できるコマンドだ。
テーブル ⇒ ファイル
一番シンプルなパターンだと、
COPY [テーブル名] TO 出力先ファイルパス
で、テーブルの全カラム全レコードがタブ区切りで出力される
ファイル ⇒ テーブル
同じく、まずは一番シンプルに
COPY [テーブル名] FROM 入力元ファイルパス
で、ファイルの全データがインポートされる。
ファイルのデータ順とテーブルのカラム順が一致していなかったり、特定のカラムだけ取り込みを行いたい場合は、テーブル名の後ろにカラム名を指定すればできます。
注意点
ファイルパスはPostgreSQLが参照可能なディレクトリである必要があります。
DBとクライアントとなるアプリがそれぞれ別サーバに配置されている場合、指定されるファイルパスをDBサーバが参照可能なディレクトリとする必要があります。
上記構成の場合、DBサーバとアプリサーバで共通で参照可能なディレクトリを共有しておく必要があります。
@ConfigurationPropertiesの実装メモ
- .propertiesからの設定値を読み出す際に使用する。
- prefix で、読み出し範囲の絞り込みが可能。
- setterが必要。
- @ConfigurationPropertiesを設定したクラスを継承しても、親の設定値は注入されない。
- getter/setterの名称は、get/set[プロパティ名の先頭のみ大文字、以下はプロパティ名と同じ]となる。
- camel記法の場合は、アンダーバーの後ろの文字を大文字にすることでも読み込んでくれたはず。
Spring bootでGZIPリクエスト/レスポンスに対応する
Spring bootでGZipを扱う際は、リクエストへの対応かレスポンスへの対応かで対応方法が異なります。
* やりたいこと | * 対応方法 |
GZIPリクエストに対応する | Filter(GZipFilter)の実装 |
GZIPでレスポンスを返す | 設定の変更 |
GZIPでレスポンスを返す
Appendix A. Common application properties
server.compression.enabled=false # Whether response compression is enabled.
の設定をtrueにしてやれば、とりあえずGZIP形式でのレスポンスとなる。
有効無効の設定がアプリ全体での切り替えになるので、I/F次第では注意が必要。
Super CSVを使ってStringをCSVオブジェクトに変換する
CSVの解析は、ちゃんとやろうとすると意外と大変だったりします。
カンマでsplitするだけ、と思っていてはダメで、ダブルクォーテーションによる区切りとそのエスケープまでしないといけません。面倒です。
なので、ライブラリを使用するのが望ましいです。
CSVの読み書きをする際に使用しました。
StringからCSVオブジェクトへ変換したかったのですが、例を見かけなかったので整理。
#StringReaderに気づけば、わざわざ悩む内容ではない。。?
CsvListReader csvListReader = new (new StringReader("csv, string, hogehoge"), preferences);
PostgreSQLでHEX文字列を符号ありの数値に変換する
HEX文字列から数値への変換処理は、PostgreSQLのSQLでもできます。
4Byte(8文字)、8Byte(16文字)
4Byte、8Byteの変換は、下記Stack Overflowで回答がつけられています。
そちらを参照のこと。
stackoverflow.com
2Byte(4文字)
自分がやりたかったのは2ByteのHEX文字列変換だったので、上記回答を参考に、以下のようにすることでできました。
SELECT (((('x' || lpad(hex, 4, '0'))::bit(16))::bit(32)::int4)>>16)::int2::numeric(16,4) as short_val FROM ( VALUES ('1'::text) ,('f') ,('100') ,('7fff') ,('8000') ,('dead') ,('ffff') ) AS t(hex); ||< 結果 >|| short_val ------------ 1 15 256 32767 -32768 -1