第51回nds postgresqlのデータ型 #nds51
TRANSCRIPT
経緯• 数値・文字列・日付ぐらいしか使ってないぞ• ざっとドキュメントを眺めていた
• 使ったことのないデータ型がたくさんあるぞ• 特に PostgreSQL はデータ型が多い
• もっとデータベースの機能を使ったほうが良いのでは?• 日付を 8 桁の文字で保持→ DATE 型• ???→ PostgreSQL が用意するデータ型
思ったより大差なかった・・・• PostgreSQL にしかないと思っていた型
• 最近のバージョンではサポートしている• 例: JSON
• postgresql 9.2 • mysql 5.7• sqlserver 2016
json 型• postgresql 9.2 以降• mysql 5.7• sqlserver 2016• oracle チェック制約のみ
CREATE TABLE mytable( id integer, info json);
jsonb 型• postgresql 9.4 以降• json をバイナリで保持• 検索効率向上• 各種関数・演算子のサポート• インデックスサポート
CREATE TABLE mytable( id integer, info jsonb);
なんでも JSON にするのは危険• 1 つの列に複数の意味のデータを入れるべきではない• 制約が使えない・外部キーが使えない• クエリが複雑になる• 検索や関連に使われないデータのみに使用したい
参考 : PostgreSQL のアンチパターン : 何でもかんでも json に入れるhttps://yakst.com/ja/posts/2445
本の情報 booksCREATE TABLE books( id integer PRIMARY KEY, name text, price integer, info jsonb);
INSERT INTO books VALUES(1, 'book1', 45745, '{"format": "text", "tags": ["english","game","kids"]}'::jsonb),
検索SELECT * FROM books WHEREinfo @> '{"format": "pdf"}'::jsonb;
format が pdf の本を検索
@> 包含演算子JSON パス・値を保持するか
感想• text に json 入れるよりは JSON 検証も行われる• インデックスや各種関数、演算子が使えて便利• id, key, value だけのテーブル (EAV) よりマシか• 外部キーは絶対マズい• 検索条件に使うのは要件次第か用法用量を守ろう
範囲型• 数値
• int4range: 4 バイト整数範囲• int8range: 8 バイト整数範囲• numrange: 実数範囲
• 日付• tsrange: 日付時刻範囲• tszrange: 日付時刻範囲(タイムゾーンつき)• daterange: 日付範囲
境界が明確• 3 以上 7 以下(閉じた境界)
• '[3, 7]'::int4range
• 3 以上 7 未満(開いた上限)• '[3, 7)'::int4range
• 3 以上(境界のない上限)• '[3, ]'::int4range
3 7
3 7
3
重なりや含有をチェックする演算子• 含有'[10, 20]'::int4range @> 14
• 重なり'[10, 20]'::int4range && '[15, 30]'::int4range
10 2014
10 2015 30
予約テーブル• EXCLUDE の排他制約で予約の重複を防ぐ制約CREATE TABLE reservation( during daterange, EXCLUDE USING GIST(during WITH &&));
INSERT INTO reservation VALUES('[2017-03-10, 2017-03-12]');
INSERT INTO reservation VALUES('[2017-03-11, 2017-03-13]');-- 重なりがあるのでエラー
↑during の重なりの排他
プログラミング言語から• プログラミング言語から DB を使うときには様々な
ライブラリを通している• DB アクセス用 API• OR マッパーなどの便利ライブラリ
• 標準 API は各種 DB の最小公倍数的な機能• JDBC API で提供されるデータ型に JSON はない
Java の例
PostgreSQL
JavaApplication
JDBC Driverfor PostgreSQL
JDBCAPI
EclipseLinkJPA
PostgreSQL 特有の機能はネイティブなところまで
どうやって使うのか• ネイティブ対応しているレイヤーを使用
• JDBC for PostgreSQL• テキスト経由で受け渡し→ SQL で型変換• PostgreSQL方言
• ライブラリ側の対応次第
具体的に考えてみる• jsonb 型を Java / Python から利用
CREATE TABLE json_test( id integer, info jsonb)
id info1 {"a": 100, "b": "Hello"}2 {"a": 200, "b": "World"}
JDBC API ( 低レベル API)
resultSet.getString("info");
// {"a": 100, "b": "Hello"}
SELECT
ResultSet#getString で文字列として取得可能
JDBC API ( 低レベル API)INSERT
PGobject を使用。 ※ postgresql ドライバの機能import org.postgresql.util.PGobject;
PGobject pgobj =new PGobject();pgobj.setValue(json_string); //json 文字列pgobj.setType("jsonb");
preparedStatement.setObject(1, pgobj);Java 的にはベンダー製パッケージの import はちょっと抵抗がある
JDBC API ( 低レベル API)INSERT
SQL 文内でキャストPreparedStatement ps =conn.prepareStatement( "INSERT INTO json_test(info) VALUES(jsonb(?))");
ps.setString(1, json_string); //json 文字列
JPA (高レベル API)SELECT / INSERT
Converter を作成すればSQL データ型と Java データ型を相互変換できる
@Entity@Table(name = "json_test")public class JsonTest implements Serializable { // ... 中略 ...
@Column(name = "info") @Convert(converter = JsonbConverter.class) private String info;
Python の場合https://github.com/civic/postgresql-python-json
Python の例
PostgreSQL
PythonApplication
psycopg2DB API
SQLAlchemy
PostgreSQL 特有の機能はネイティブ対応なところまで
libpq
psycopg2 ( 低レベル API)
cur.execute("SELECT info FROM json_test WHERE id=1")row = cur.fetchone()print(row[0])# {'b': 'Hello', 'a': 100}
SELECT
何もしなくても、 dict(辞書型 ) として取得できる
psycopg2 ( 低レベル API)INSERT
psycopg2.extras.Json を使用し、 dict→Json
from psycopg2.extras import Json
#json object で更新cur.execute( "INSERT INTO json_test(info) VALUES(%s)", [ Json({"a":30, "b": "update"}) ])
psycopg2 ( 低レベル API)INSERT
SQL 文内でキャスト。import json
# dict を文字列化して SQL に渡すcur.execute( "INSERT INTO json_test(info) VALUES(jsonb(%s))", [ json.dumps({"a":30, "b": "update"}) //dict→str ])
SQLAlchemy (高レベル API)SELECT / INSERT
SQLAlchemy は至れり尽くせり。各 DB ベンダー用方言 (dialect) が用意されている。
from sqlalchemy.dialects.postgresql import JSONB
class JsonTest(Base): __tablename__ = 'json_test' id = Column(Integer, primary_key=True) info = Column(JSONB) # JSONB 型の列と宣言 ...
まとめ• PostgreSQL 特有のデータ型が便利そう正規化を失わないように• プログラミング言語からも pg 特有の型は使えそう
• Java• 標準 API のままでは使いにくい• ベンダー実装の機能を使うか型変換で
• Python• ダックタイピングできるので Java ほど厳しくない• SQLAlchemy 便利
参考• PostgreSQL 9.6.2文書• PGCon2014 JSONB データ型を使ってみよう• Oracle® Database SQL言語リファレンス• MySQL 5.7 Reference Manual• データ型 (Transact-SQL) - MSDN - Microsoft• PostgreSQLのアンチパターン : 何でもかんでもjsonに入れる• JPA 2.1 の 新機能 Converter まとめ• Psycopg - PostgreSQL database adapter for Python