KDOC 656: データベースで列の並び順によってサイズが変わる

この文書のステータス

  • 作成
    • 2026-10-11 貴島
  • レビュー
    • 2026-10-11 貴島

概要

データベースでも、プログラミング言語と同様に、フィールドのデータ型を定義する順序によって使うデータサイズが変わりうる。

型ごとに、値を置く位置の境界(整列)が決まっている。境界に合わないときは、前にパディングが入る。

ディスク上の完全な行の表現のように、複数の値が隣接して格納される際には、指定された境界で始まるように、この型のデータの前にパディングが挿入されます。

52.64. pg_type

同じ列を、並び順だけ変えた2つのテーブルで比べる。boolean は1バイトで整列なし、bigint は8バイトで8バイト境界(double 整列)である。

-- boolean と bigint を交互に並べる
CREATE TABLE padded
  (a boolean, b bigint, c boolean, d bigint);
-- bigint を先に、boolean をあとにまとめる
CREATE TABLE packed
  (b bigint, d bigint, a boolean, c boolean);

INSERT INTO padded VALUES (true, 1, false, 2);
INSERT INTO packed VALUES (1, 2, true, false);

-- 列ごとの長さと整列
SELECT format('%s : %s : 長さ %s : 整列 %s',
              attname, typname, typlen, typalign)
       AS cols
FROM pg_attribute a
JOIN pg_type t ON t.oid = a.atttypid
WHERE attrelid = 'padded'::regclass
  AND attnum > 0
ORDER BY attnum;

-- 1行の大きさ(行ヘッダを含む)
SELECT pg_column_size(p.*) AS padded FROM padded p;
SELECT pg_column_size(p.*) AS packed FROM packed p;
|                                                       |
|-------------------------------------------------------|
| > CREATE TABLE padded                                 |
| >   (a boolean, b bigint, c boolean, d bigint);       |
| CREATE TABLE                                          |
|                                                       |
| > CREATE TABLE packed                                 |
| >   (b bigint, d bigint, a boolean, c boolean);       |
| CREATE TABLE                                          |
|                                                       |
| > INSERT INTO padded VALUES (true, 1, false, 2);      |
| INSERT 0 1                                            |
|                                                       |
| > INSERT INTO packed VALUES (1, 2, true, false);      |
| INSERT 0 1                                            |
|                                                       |
| > SELECT format('%s : %s : 長さ %s : 整列 %s',        |
| >               attname, typname, typlen, typalign)   |
| >        AS cols                                      |
| > FROM pg_attribute a                                 |
| > JOIN pg_type t ON t.oid = a.atttypid                |
| > WHERE attrelid = 'padded'::regclass                 |
| >   AND attnum > 0                                    |
| > ORDER BY attnum;                                    |
| cols                                                  |
| a : bool : 長さ 1 : 整列 c                            |
| b : int8 : 長さ 8 : 整列 d                            |
| c : bool : 長さ 1 : 整列 c                            |
| d : int8 : 長さ 8 : 整列 d                            |
|                                                       |
| > SELECT pg_column_size(p.*) AS padded FROM padded p; |
| padded                                                |
| 56                                                    |
|                                                       |
| > SELECT pg_column_size(p.*) AS packed FROM packed p; |
| packed                                                |
| 42                                                    |

padded は1行 56 バイト、packed は 42 バイトで、14 バイト違う。padded では、boolean(1バイト)のあとの bigint を8バイト境界へ置くため、7バイトのパディングが2回入る。その 7 × 2 = 14 バイトが差になる。

※行ヘッダを除く

padded (boolean, bigint, boolean, bigint) : 32 バイト
0        8        16       24       32
|a.......|bbbbbbbb|c.......|dddddddd|
  ^^^^^^^           ^^^^^^^
  パディング 7      パディング 7

packed (bigint, bigint, boolean, boolean) : 18 バイト
0        8        16 18
|bbbbbbbb|dddddddd|ac|

a, c = boolean(1バイト)  b, d = bigint(8バイト、8バイト境界)  . = パディング

関連