Day3 前半のゴール
「“なんとなく文字列・なんとなく日付”から卒業する」
今日は PostgreSQL の代表的なデータ型のうち、
文字列(TEXT / VARCHAR)、真偽値(BOOLEAN)、日時(TIMESTAMP)に絞って話します。
前半のゴールはこうです。
TEXT と VARCHAR の違いを、「どっちをいつ選ぶか」というレベルで説明できる。
BOOLEAN が「0/1 ではなく true/false を素直に扱える型」だと理解できる。
TIMESTAMP が「ただの文字列日付」ではなく、「時間として計算できる値」だとイメージできる。
SQLite や MySQL で「とりあえず TEXT」「とりあえず VARCHAR(255)」と書いていた感覚を、
PostgreSQL では一段整理していきます。
TEXT / VARCHARの違いを整理する
「“技術的な違い”より“設計としての意図”が大事」
まずは、文字列型です。
PostgreSQL では、代表的にこういう型があります。
TEXT
VARCHAR(n)
VARCHAR(長さ指定なし)
SQLite では「全部 TEXT でいいや」となりがちでした。
MySQL では「とりあえず VARCHAR(255)」がよく出てきたと思います。
PostgreSQL では、実は TEXT と VARCHAR(長さ指定なし)は、性能面ではほぼ同じように扱われます。
「TEXTだから遅い」「VARCHARだから速い」といった差は、基本的に気にしなくてOKです。
じゃあ何が違うのかというと、「長さ制約を付けるかどうか」です。
VARCHAR(n) は「長さの上限を設計として決める」ためのもの
例えば、ユーザー名を入れるカラムを考えます。
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL
);
SQLここで VARCHAR(50) と書くのは、「ユーザー名は最大50文字まで」という“ルール”をDB側に持たせる、という意味です。
もしアプリ側のバリデーション漏れで、100文字の名前が飛んできたら、
PostgreSQLは「長すぎるよ」とエラーを返してくれます。
一方で、ブログ記事の本文や、エラーログのメッセージなど、
「長さをあまり気にせず、好きなだけ入れたい」ものは TEXT にするのが自然です。
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title VARCHAR(200) NOT NULL,
body TEXT NOT NULL
);
SQLtitle は「ある程度の上限を決めたい」ので VARCHAR(200)、
body は「長くてもいい」ので TEXT、という分け方です。
ここでの重要ポイントは、
「TEXTかVARCHARかは性能の話ではなく、“どこまでをDBに守らせるか”という設計の話」
だと捉えることです。
BOOLEAN型をちゃんと使う意味
「0/1 や ‘Y’/’N’ から、“true/false”の世界へ」
次に BOOLEAN 型です。
SQLite では BOOLEAN という型は実質なく、0/1 や TEXT で代用していたはずです。
MySQL でも、BOOLEAN は実体としては TINYINT(1) だったりします。
PostgreSQL には、ちゃんと BOOLEAN 型があります。
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
is_active BOOLEAN NOT NULL
);
SQLここにデータを入れるとき、PostgreSQLではこう書けます。
INSERT INTO users (name, is_active)
VALUES ('Taro', true),
('Jiro', false);
SQLtrue / false を、そのまま値として扱えます。
また、0/1 や ‘t’/’f’ なども受け付けてくれますが、
「読みやすさ」という意味では true / false を素直に使うのがおすすめです。
条件式が“人間の言葉”に近づく
BOOLEAN をちゃんと使うと、WHERE句も読みやすくなります。
SELECT * FROM users WHERE is_active = true;
SQLあるいは、もっとシンプルに、
SELECT * FROM users WHERE is_active;
SQLと書くこともできます(is_active が true の行だけが返る)。
MySQLで「フラグを TINYINT(1) で持って、0/1 で判定する」スタイルに慣れていると、
最初は違和感があるかもしれませんが、
「is_active が true かどうか」と書けるのは、かなり気持ちがいいです。
ここでのポイントは、
「BOOLEAN をちゃんと使うと、SQLが“ビジネスルールの文章”に近づく」
ということです。
TIMESTAMP型のイメージを固める
「“文字列の日付”ではなく、“時間として計算できる値”」
最後に TIMESTAMP です。
これは「日時」を表す型で、PostgreSQLでは大きく2種類あります。
TIMESTAMP(タイムゾーンなし)
TIMESTAMPTZ(タイムゾーン付き)
Day3 前半では、まず「タイムゾーンなしのTIMESTAMP」を中心にイメージを固めます。
例えば、ユーザーの作成日時・更新日時を持つテーブルは、こう書けます。
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL
);
SQLここにデータを入れるとき、よくやるのは NOW() を使う方法です。
INSERT INTO users (name, created_at, updated_at)
VALUES ('Taro', NOW(), NOW());
SQLNOW() は「現在の日時」を TIMESTAMP 型で返してくれます。
文字列ではなく“時間として扱える”ことが大事
TIMESTAMP の良さは、「時間として計算できる」ことです。
例えば、「直近7日以内に作成されたユーザーを取りたい」とします。
SELECT *
FROM users
WHERE created_at >= NOW() - INTERVAL '7 days';
SQLここでは、「現在時刻から7日引いた日時」以上のもの、という条件を書いています。
これが、もし created_at を TEXT で持っていたら、こういう計算はかなり面倒になります。
PostgreSQLでは、「日時はTIMESTAMPで持つ」「期間はINTERVALで表す」というのが基本スタイルです。
これに慣れると、「時間に関するクエリ」が一気に書きやすくなります。
Day3 前半のまとめ
PostgreSQLの文字列型では、TEXT と VARCHAR は性能差よりも「長さ制約をDBに持たせるかどうか」が本質で、ユーザー名やタイトルのように上限を決めたいものは VARCHAR(n)、本文やログのように長さをあまり気にしないものは TEXT として、「どこまでをDBに守らせるか」を設計として決める。
BOOLEAN 型は、0/1 や ‘Y’/’N’ の代わりに true / false をそのまま扱える型で、is_active のようなフラグを BOOLEAN で定義すると、WHERE is_active のように“人間の言葉に近い条件式”を書けるようになり、SQLの可読性が上がる。
TIMESTAMP は「文字列の日付」ではなく「時間として計算できる値」であり、created_at TIMESTAMP に NOW() を入れておけば、WHERE created_at >= NOW() - INTERVAL '7 days' のように「直近7日」のような条件を自然に書けるようになる――この3つの型のイメージをしっかり持つことが Day3 前半の着地点になる。
