Day13 前半のゴール
「“SQLの中でも配列を持てる”感覚を手に入れる」
今日は、PostgreSQLのもうひとつの武器「配列型(ARRAY)」です。
JSONと似ているところもありますが、「型がはっきりしている」「もっと軽く扱える」という違いがあります。
前半のゴールはこうです。
ARRAY型が「同じ型の値をまとめて持つための箱」だと理解できる。
配列カラムを持つテーブルを作るイメージが持てる。
基本的な配列リテラルと、要素の取り出し・長さ取得の感覚をつかめる。
ARRAY型とは何か
「“同じ型の値”をまとめて1カラムに入れる仕組み」
まずはイメージからいきます。
プログラミングでいう「配列」「リスト」と同じで、「同じ型の値を複数まとめて持つ」ためのものです。
例えば、「ユーザーが複数のタグを持っている」ケースを考えます。
premiumbeta_userdark_mode_enabled
これを、普通にやるなら「user_tags テーブルを作って、1タグ=1行」で持ちます。
それも正しいやり方ですが、PostgreSQLなら「タグを配列として1カラムにまとめて持つ」という選択肢も取れます。
ARRAY型は、「型がはっきりしている」のがポイントです。text[] なら「文字列の配列」、integer[] なら「整数の配列」というふうに、
「何の配列か」がスキーマに書かれます。
JSONは「中身の型がバラバラでもOK」ですが、ARRAYは「同じ型だけ」です。
その代わり、軽くて速くて、扱いがシンプルです。
配列カラムを持つテーブルを作ってみる
「タグを text[] で持つユーザーテーブル」
具体的なテーブルを1つ作ってみましょう。
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
tags TEXT[] NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
SQLここでのポイントを分解します。
tags TEXT[]
文字列の配列(text[])としてタグを持つカラム。
DEFAULT '{}'
空配列をデフォルト値にしている。
NULLではなく「要素ゼロの配列」として扱えるので、後で楽。
この tags に、例えばこういう値が入ります。
ユーザーA:{"premium", "beta_user"}
ユーザーB:{"free"}
ユーザーC:{}(タグなし)
PostgreSQLの配列リテラルは {...} という形をしていて、
中にカンマ区切りで要素を書きます。
配列リテラルの基本
「INSERT時に {} で書く感覚をつかむ」
実際にINSERTするSQLを見てみます。
INSERT INTO users (email, name, tags)
VALUES (
'taro@example.com',
'Taro',
ARRAY['premium', 'beta_user']
);
SQLまたは、こう書くこともできます。
INSERT INTO users (email, name, tags)
VALUES (
'hanako@example.com',
'Hanako',
'{"free"}'
);
SQLここでの違いはこうです。
ARRAY['premium', 'beta_user']
SQLの配列コンストラクタ。
中の '...' は text 型で、結果は text[]。
'{"free"}'
配列リテラルを文字列として書いて、PostgreSQLが text[] に解釈する。
どちらも最終的には同じ text[] になります。
初心者的には、ARRAY[...] の方が「配列を作っている感」が分かりやすいと思います。
空配列を入れたいときは、こうです。
INSERT INTO users (email, name, tags)
VALUES (
'no-tags@example.com',
'NoTags',
ARRAY[]::text[]
);
SQLARRAY[] だけだと「型が分からない」ので、::text[] で「textの配列」と明示しています。
ここは少しだけPostgreSQLっぽさが出るところです。
配列の要素を取り出す
「tags[1] で“1番目のタグ”を取る」
配列から要素を取り出すのは、とてもシンプルです。
PostgreSQLの配列は「1始まり」です(ここ、JSONと違うので注意)。
例えば、こういう行があるとします。
tags = {"premium", "beta_user", "dark_mode_enabled"}
このとき、「1番目のタグ」を取りたいなら、こう書きます。
SELECT
tags[1] AS first_tag,
tags[2] AS second_tag
FROM users
WHERE email = 'taro@example.com';
SQL結果は、
first_tag → premiumsecond_tag → beta_user
というふうになります。
ここでの重要ポイントは、「インデックスが1から始まる」ことです。
プログラミング言語の多くは0始まりなので、
「配列だけど1始まり」というPostgreSQLの癖を、ここで意識しておいてください。
配列の長さを知る
「何個タグを持っているか」を数える
配列の長さ(要素数)を知るには、cardinality や array_length を使います。
例えば、「ユーザーごとのタグ数」を見たいとします。
SELECT
email,
tags,
cardinality(tags) AS tag_count
FROM users
ORDER BY tag_count DESC;
SQLcardinality(tags) は、「配列の要素数」を返します。
結果は例えばこうなります。
email | tags | tag_count
-------------------+----------------------------------------+----------
taro@example.com | {"premium","beta_user","dark_mode..."} | 3
hanako@example.com | {"free"} | 1
no-tags@example... | {} | 0
array_length(tags, 1) でも同じようなことができます。
SELECT
email,
array_length(tags, 1) AS tag_count
FROM users;
SQL第2引数の 1 は「次元」を表していて、
1次元配列なら「1」でOKです(多次元配列は後半の話)。
「配列の長さをSQLで取れる」というのは、
「配列をただの文字列として持っている」のとは大きな違いです。
配列型とJSON配列の違いを整理する
「“型が決まっている軽い配列”か、“何でも入る柔軟な配列”か」
ここまでで、「ARRAYってJSON配列と似てない?」と思ったはずです。
似ていますが、使いどころが少し違います。
ARRAY(text[] や integer[])
型が決まっている(全部text、全部integerなど)。
軽くて速い。
演算子や関数がシンプル。
「同じ種類の値を少しだけまとめて持ちたい」ときに向いている。
JSON配列(jsonbの中の [...])
中身の型がバラバラでもOK。
構造が複雑でもOK(オブジェクトの配列など)。
柔軟だが、重めで、扱いも少し複雑。
「構造化されたデータを柔軟に持ちたい」ときに向いている。
例えば、「タグのような単純な文字列の集まり」は、ARRAYで十分です。
「デバイス情報のような、複数のキーを持つオブジェクトの集まり」は、JSON配列の方が向いています。
Day13 前半では、「ARRAYは“軽い・型付きの配列”、JSON配列は“柔軟・構造付きの配列”」というざっくりした違いを持っておいてくれれば十分です。
配列型を使うときの“最初の一歩の設計感覚”
「“単純なリスト”ならARRAYで持ってみる」
いきなり何でもARRAYにする必要はありません。
でも、「これは単純なリストだな」と思ったら、ARRAYを検討する価値があります。
例えば、
ユーザーのタグ(text[])。
記事の関連キーワード(text[])。
通知の送信先IDリスト(integer[])。
こういう「同じ型の値が複数あるだけ」のものは、
わざわざ別テーブルにしてもいいし、ARRAYでまとめてもいい領域です。
PostgreSQLはARRAYをちゃんとサポートしているので、
「RDBだから配列はダメ」という固定観念は、一度外してみていいと思います。
Day13 前半のまとめ
PostgreSQLの配列型(ARRAY)は、「同じ型の値をまとめて1カラムに入れる」ための仕組みで、text[] や integer[] のように「何の配列か」がスキーマに書かれる。
ユーザーのタグを tags TEXT[] のようなカラムで持ち、ARRAY['premium','beta_user'] や '{"free"}' のようなリテラルでINSERTし、tags[1] で1番目の要素を取り出したり、cardinality(tags) や array_length(tags,1) で「何個タグを持っているか」を数えたりできる。
ARRAYは「型が決まっていて軽い配列」、JSON配列は「柔軟で構造を持つ配列」という違いがあり、タグやキーワードのような“単純なリスト”はARRAYで持つと扱いやすくなる――これが Day13 前半の着地点になる。
