導入
「小計=単価×数量」のように、他の列から計算で決まる値があります。これを毎回アプリで計算して保存すると、単価を直したのに小計を直し忘れる、といった食い違いが起きます。生成列(generated column) を使えば、DB が自動で計算し、常に整合した値を返してくれます。
説明
列名 型 GENERATED ALWAYS AS (式) と定義すると、その列は式の計算結果になります。INSERT のときに値を指定する必要はありません(してはいけません)。
subtotal INTEGER GENERATED ALWAYS AS (price * quantity)
初期表示の例では、単価 300 × 数量 4 から subtotal が自動で 1200 になります。price や quantity を後で更新すれば、subtotal も自動で追随します。「1つの事実は1か所に」という正規化の精神を、計算値にも広げたものです。
flowchart LR
P["price × quantity"] -->|"GENERATED ALWAYS AS"| S["subtotal(自動計算)<br/>手で入れない・ズレない"]
生成列には2種類あります。VIRTUAL(既定・読むたびに計算、保存しない)と STORED(計算結果を保存しておく)。読み取りが多いなら STORED、書き込みが多く容量を節約したいなら VIRTUAL、と使い分けます。いずれにせよ「派生する値は手で持たず、式で定義する」のが、壊れにくい設計のコツです。
やってみよう
初期表示のコードを実行し、subtotal に自動で 1200 が入っていることを確認しましょう。次に INSERT INTO line_item (id, price, quantity) VALUES (2, 500, 3); を追加して、subtotal が 1500 になることを見てください。手で計算していないのに、いつも正しい値が出ます。
演習
rect(長方形)というテーブルを作ってください。列は id(主キー)、w(INTEGER)、h(INTEGER)、そして面積 area を w * h の生成列にします。1件 (1, 4, 5) を入れて、area が 20 になることを確かめてください。
ヒント1を見る
area INTEGER GENERATED ALWAYS AS (w * h) と定義します。INSERT では area を指定しません。
ヒント2を見る
CREATE TABLE rect (id INTEGER PRIMARY KEY, w INTEGER, h INTEGER, area INTEGER GENERATED ALWAYS AS (w * h));