PostgreSQL | SQLite+MySQL経験者向け、30日で習得するPostgreSQL:差分理解 - Day1 MySQLとの違い

SQL PostgreSQL
スポンサーリンク

Day2 前半のゴール

「“PostgreSQLはちょっと真面目なやつ”という感覚をつかむ」

今日は、SQLite と MySQLを触ってきたあなたに向けて、
「PostgreSQLって、どこが違うの?」を一番大事な3点に絞って話します。

厳密な型システム
NULLの扱い
標準SQLへの準拠度

前半のゴールはこうです。
PostgreSQLが「型にうるさい」「NULLに正直」「標準SQLにかなり忠実」な性格だと理解する。
MySQLで“なんとなく許されていたこと”が、PostgreSQLでは怒られるイメージを持つ。
その違いが「めんどくさい」ではなく、「バグを早めに見つけてくれる頼れる相棒」だと感じられる。


厳密な型システムとは何か

「“とりあえず文字列に突っ込んでおく”を許さない世界」

SQLite は「ほぼ何でも TEXT に入るし、型もゆるい」世界でした。
MySQLも、「文字列を数値カラムに入れようとすると、勝手に変換してくれる」ことが多かったはずです。

PostgreSQLは、ここがかなり違います。
一言で言うと、「型に対して厳格で、変なものは基本的に拒否する」タイプです。

例えば、こういうテーブルがあるとします。

CREATE TABLE sample (
  id    INTEGER,
  price INTEGER
);
SQL

ここで、MySQLだとこんなINSERTが「通ってしまう」ことがあります。

INSERT INTO sample (id, price) VALUES (1, '100');
SQL

MySQLは '100' を数値に変換してくれるので、特にエラーにならず入ります。
しかし、PostgreSQLは「文字列を整数に勝手に変換する」ことをあまりしません。
場合によっては、こういうINSERTでエラーになります。

「型が違うものは、ちゃんと直してから入れてね」というスタンスです。

この厳しさは、最初は「うわ、めんどくさい」と感じるかもしれません。
でも、実務ではむしろメリットです。

「本当は数値を入れるべきところに、うっかり文字列を渡していた」
「日付カラムに、フォーマットがおかしい文字列を入れようとしていた」

こういうバグを、PostgreSQLは早い段階で止めてくれます。


型の“真面目さ”が効いてくる具体例

「日付・時刻・JSONなど、“ちゃんとした型”が豊富」

PostgreSQLは、型の種類もかなり豊富です。
日付・時刻・タイムゾーン付き時刻、JSON、配列など、MySQLよりも「型としてちゃんと用意されているもの」が多いです。

例えば、日付と時刻を扱うときに、こういうテーブルを作れます。

CREATE TABLE events (
  id         SERIAL PRIMARY KEY,
  title      TEXT NOT NULL,
  start_at   TIMESTAMPTZ NOT NULL
);
SQL

TIMESTAMPTZ は「タイムゾーン付きのタイムスタンプ」です。
日本時間・UTC・他の国の時間を意識したシステムでは、これがかなり重要になります。

ここに、変な文字列を入れようとすると、PostgreSQLはちゃんと怒ります。

INSERT INTO events (title, start_at)
VALUES ('テスト', '2024-13-99 99:99:99');
SQL

これは「存在しない日付・時刻」なので、エラーになります。
MySQLだと、場合によっては「0000-00-00 00:00:00」のような“謎の値”として入ってしまうことがあります。

PostgreSQLは、「おかしいものはおかしい」と言ってくれるので、
「変な値が静かに紛れ込む」リスクが減ります。


NULLの扱いが“正直”という話

「NULLは“何も分からない”であって、“0や空文字”ではない」

次に、NULLの話です。
これは、SQLite・MySQL経験者でもよく混乱するポイントです。

NULLは、「値がない」「分からない」という状態を表します。
0でも空文字でもなく、「そもそも値が存在しない」という意味です。

PostgreSQLは、このNULLの意味をかなり真面目に扱います。
特に、比較演算や集計のときに、その“正直さ”が顔を出します。

例えば、こういうテーブルがあるとします。

CREATE TABLE people (
  id    INTEGER,
  age   INTEGER
);
SQL

ここに、こんなデータが入っているとします。

id=1, age=20
id=2, age=NULL

「20歳以上の人を取りたい」として、こう書いたとします。

SELECT * FROM people WHERE age >= 20;
SQL

このとき、PostgreSQLは「age=NULL の行は、条件に合致しない」と判断します。
なぜなら、「NULL >= 20 は、真でも偽でもなく“不明”」だからです。

NULLは「分からない」なので、「20以上かどうかも分からない」という扱いになります。
その結果、「age>=20 の条件には含めない」という動きになります。

MySQLでも基本は同じですが、設定やモードによっては挙動が変わることがあります。
PostgreSQLは、このあたりがかなり一貫していて、「NULLはとにかく分からないもの」として扱います。


NULLと比較するときの“お約束”

「= NULL ではなく IS NULL / IS NOT NULL を使う」

NULLの扱いで一番大事なルールは、「NULLとの比較には = を使わない」ということです。

例えば、「年齢が未設定の人を取りたい」ときに、こう書いてしまいがちです。

SELECT * FROM people WHERE age = NULL;
SQL

これは、PostgreSQLでは「常に偽」になります。
なぜなら、「NULL = NULL も“不明”」だからです。

正しい書き方はこうです。

SELECT * FROM people WHERE age IS NULL;
SQL

逆に、「年齢が設定されている人」を取りたいときは、

SELECT * FROM people WHERE age IS NOT NULL;
SQL

と書きます。

この「IS NULL / IS NOT NULL を使う」というルールは、標準SQLの世界では当たり前ですが、
MySQLだけを使っていると、意外と意識されていないことがあります。

PostgreSQLは標準SQLにかなり忠実なので、「NULLとの比較はISを使う」というルールを守らないと、普通にハマります。
ここは、Day2の超重要ポイントです。


標準SQLへの準拠度が高いとはどういうことか

「“他のDBでも通じる書き方”を身につけやすい」

最後に、「標準SQLへの準拠度」の話をします。

SQLには、「こう書くべき」という標準仕様(SQL標準)が存在します。
ただ、現実には各DBが独自拡張を入れていて、「方言」がたくさんあります。

MySQLは、かなり方言が多いDBです。
LIMIT の書き方、AUTO_INCREMENTENUM 型、INSERT ... ON DUPLICATE KEY UPDATE など、
「MySQLでしか通じない書き方」がたくさんあります。

PostgreSQLも独自機能はありますが、基本的な部分では標準SQLにかなり忠実です。
例えば、

NULLの扱い(3値論理)
トランザクションの振る舞い
JOINやサブクエリの書き方

などは、「教科書的なSQL」とほぼ同じです。

この「標準に近い」ということは、
「PostgreSQLで身につけたSQLの書き方は、他のDBでも通じやすい」
という意味でもあります。

SQLite → MySQL → PostgreSQLと来たあなたは、
「方言の多いMySQL」と「標準寄りのPostgreSQL」の両方を知ることになります。
これは、エンジニアとしてかなり強い武器になります。


Day2 前半のまとめ

PostgreSQLは、MySQLに比べて「型に厳格」で、整数カラムに変な文字列を入れようとするとエラーになるなど、“おかしな値を静かに受け入れない”性格を持ち、日付・時刻・JSON・配列などの豊富な型を真面目に扱うことで、バグを早めに炙り出してくれる。
NULLについては、「値がない/分からない」という意味を3値論理で一貫して扱い、age >= 20 の条件に age=NULL の行は含めず、「NULLとの比較には = NULL ではなく IS NULL / IS NOT NULL を使う」という標準SQLのルールを守らないと普通にハマる世界であり、全体として標準SQLへの準拠度が高いため、PostgreSQLで身につけた“正統派のSQLの書き方”は他のDBにも持ち運びやすい――という「ちょっと真面目なやつ」という性格をつかむのが Day2 前半の着地点になる。

タイトルとURLをコピーしました