PostgreSQLで配列を扱う
パフォーマンス改善のために、今までアプリでやっていた処理をDBに寄せることになりした。その一環でSQLで配列を使った処理を書いたので、まとめました。
詳しくは公式ドキュメントを参照ください。
9.18. 配列関数と演算子
環境
- PostgreSQL 11.6
使用するテーブル
| create table users( | |
| id BIGSERIAL, | |
| first_name VARCHAR(20) NOT NULL, | |
| last_name VARCHAR(20) NOT NULL, | |
| first_name_kana VARCHAR(20), | |
| last_name_kana VARCHAR(20), | |
| version BIGINT NOT NULL, | |
| created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| deleted_at TIMESTAMP, | |
| PRIMARY KEY (id) | |
| ); | |
| create table friends( | |
| id BIGSERIAL, | |
| user_id BIGINT NOT NULL, | |
| friend_id BIGINT NOT NULL, | |
| version BIGINT NOT NULL, | |
| created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| deleted_at TIMESTAMP, | |
| PRIMARY KEY (id), | |
| FOREIGN KEY (user_id) REFERENCES users (id), | |
| FOREIGN KEY (friend_id) REFERENCES users (id) | |
| ); |
配列として集約する
SELECT
ARRAY_AGG(friend_id) AS friend_ids
FROM
friends
GROUP BY
user_idARRAY_AGG で配列を生成できます。
ソートして集約する
SELECT
ARRAY_AGG(friend_id ORDER BY created_at) AS friend_ids
FROM
friends
GROUP BY
user_idfriend_id を created_at でソートしています。
先頭要素だけ取得
SELECT
(ARRAY_AGG(friend_id ORDER BY created_at))[1] AS first_friend_id
FROM
friends
GROUP BY
user_id1始まりの添字でアクセスできます。要素数を超えた添字を指定した場合、NULL になります。ちなみにARRAY_AGG の中にLIMIT 1 をつけるのは動きませんでした。ARRAY_AGG(friend_id ORDER BY created_at LIMIT 1)
2番目以降だけ取得
SELECT
ARRAY_REMOVE(
ARRAY_AGG(friend_id),
(ARRAY_AGG(friend_id ORDER BY created_at))[1]
) AS other_friend_ids
FROM
friends
GROUP BY
user_idARRAY_REMOVE で要素を削除できます。ソートした先頭要素を削除することで、2番目以降の要素のみを取得できます。重複要素がある場合は全て消えるので注意が必要です。
共通要素の確認
SELECT
*
FROM (
SELECT
ARRAY_AGG(friend_id ORDER BY created_at) AS friend_ids
FROM
friends
GROUP BY
user_id
) AS foo
WHERE
friend_ids && ARRAY[1, 2]::bigint[]&& は1要素でも一致していればtrueを返します。数値の型が違う場合は必要に応じてcastします。
配列をテーブルとして展開する
以下の記事を参考にしました。
Studio ODIN - blog�����l�^�W > PostgreSQL �� �z����e�[�u���̂悤�Ɉ���

SELECT
(fields::VARCHAR[])[1]::NUMERIC AS id,
(fields::VARCHAR[])[2]::NUMERIC AS value,
FROM
UNNEST(ARRAY['{1,1}','{2,3}','{3,2}']) AS fieldsアプリで持っている情報をSQLで参照したい場合に使用できます。
おわり
SQLで配列を使う方法についてまとめてみました。
配列だけでなくJSONを操作するSQLなども試しましたが、使いすぎると可読性が落ちるので、どれくらいのパフォーマンス向上に寄与するのかを考えて使った方が良さそうだなと思いました。(ここで挙げたものも、試すだけ試して使わなかった例があります)
