hstore
を使用して一連のカスタムフィールドを格納するデータベースがあります。 hstore
をサポートしない別のデータベースにマージするために、キーを追加の列に分割したいと思います。
ユーザーは新しいカスタムフィールドを追加できるため、事前にキーの知識に依存することはできません。 "hstore-columnからの属性がビューの個別の列として表示されますか?" の答えは、私の問題には当てはまりません。
レコードのキーが他のレコードに存在しない場合は、null値の同じ列を取得する必要があります。
どうすればよいですか?
これも非常に効率的に行うことができます。ただし、SQLは呼び出し時に戻りの型を知る必要があるため、単一のステートメントではありません。したがって、2つのステップが必要です。ソリューションには多数の高度なテクニック...
_CREATE TABLE hstore_test (
id serial PRIMARY KEY
, hstore_col hstore
);
_
SELECT
以下のクロス集計ソリューションを作成した後、単純な「ブルートフォース」ソリューションの方がおそらく高速であることに気付きました。基本的に、クエリ @Denverは既に投稿された を動的に構築します:
_SELECT format(
'SELECT id, h->%s
FROM (SELECT id, hstore_col AS h FROM hstore_test) t;'
, string_agg(quote_literal(key) || ' AS ' || quote_ident(key), ', h->')
) AS sql
FROM (
SELECT DISTINCT key
FROM hstore_test, skeys(hstore_col) key
ORDER BY 1
) sub;
_
サブクエリ_(SELECT id, hstore_col AS h FROM hstore_test)
_は、h
列の列エイリアスhstore
を取得するだけです。
次の形式のクエリが生成されます。
_SELECT id, h->'key1' AS key1, h->'key2' AS key2, h->'key3' AS key3
FROM (SELECT id, hstore_col AS h FROM hstore_test) t;
_
結果:
_ id | key1 | key2 | key3
----+-------+-------+-------
1 | val11 | val12 | val13
2 | val21 | val22 |
3 | | | -- for a row where hstore_col IS NULL
_
crosstab()
キーのlotsの場合、これはmayの方が優れています。おそらく違います。テストする必要があります。結果はソリューション1と同じです。
crosstab()
関数を提供する追加の拡張tablefunc
が必要です。 これを最初に読んでください慣れていない場合:
_SELECT format(
$s$SELECT * FROM crosstab(
$$SELECT h.id, kv.*
FROM hstore_test h, each(hstore_col) kv
ORDER BY 1, 2$$
, $$SELECT unnest(%L::text[])$$
) AS t(id int, %s text);
$s$
, array_agg(key) -- escapes strings automatically
, string_agg(quote_ident(key), ' text, ') -- needs escaping!
) AS sql
FROM (
SELECT DISTINCT key
FROM hstore_test, skeys(hstore_col) key
ORDER BY 1
) sub;
_
ドル引用符 のネストされたレベルに注意してください。
空またはNULLのhstore
値を持つ行を保持するために、補助クエリで短い_CROSS JOIN
_の代わりにメインクエリでこの明示的な形式を使用します。
_LEFT JOIN LATERAL each(hstore_col) kv ON TRUE
_
関連:
次の形式のクエリが生成されます。
_SELECT * FROM crosstab(
$$SELECT h.id, kv.*
FROM hstore_test h
LEFT JOIN LATERAL each(hstore_col) kv ON TRUE
ORDER BY 1, 2$$
, $$SELECT unnest('{key1,key2,key3}'::text[])$$
) AS t(id int, key1 text, key2 text, key3 text);
_
初めて実行する前に、妥当性を検査することをお勧めします。これにより、パフォーマンスが最適化されます。
両方のソリューションは、Postgresテーブルのanyキーの 物理的制限〜1600列までの数のキーに対して機能します 。
どちらも、any形状または形式のキーに対しても機能します。識別子の最大長は で、デフォルトでは63バイトです 。
hstore関数each()
に加えて、 はすでにs.mで言及されています。 、関連する関数 skeys()
を使用してキーを識別します。
悪意のある形式のキー名による SQLインジェクション攻撃の可能性を回避するために、列名を正しく引用符で囲んでください。 quote_literal()
とquote_ident()
でそれを処理します。
私は少し遅れていることに気づきました-そして、あなたは確かにそれを理解しました-しかし、あなたがデンバー・ティモシーの答えに残したコメントを見て、私は他の皆に答えを残したいと思いました:
select (each(hstore_col)).key from hstore_test;
これにより、hstore_col
に含まれるkey
ごとに行が作成されるため、事前にキーが何であるかを知る必要はありません。
列で->
演算子を使用する必要があります( ここ を参照)。
他のレコードに同じキーがないレコードは、NULLとして表示されます。
create table hstore_test (id serial, hstore_col hstore);
insert into hstore_test (hstore_col) values ('key1=>val11, key2=>val12, key3=>val13'), ('key1=>val21, key2=>val22');
select hstore_col->'key1' as key1, hstore_col->'key2' as key2, hstore_col->'key3' as key3 from hstore_test;
┌───────┬───────┬───────┐
│ key1 │ key2 │ key3 │
├───────┼───────┼───────┤
│ val11 │ val12 │ val13 │
│ val21 │ val22 │ NULL │
└───────┴───────┴───────┘
(2 rows)
ここ も同様の答えです。