-- ->> は SQL の値(引用符なし)を返すので WHERE や表示にそのまま使える SELECT id, body ->> 'user' AS user, body ->> 'action' AS action, body ->> 'score' AS score FROM events WHERE body ->> 'action' = 'login'; SQL
JSON の特定フィールドで頻繁に絞り込むなら、生成列(generated column)に切り出してインデックスを張る。これで WHERE body ->> 'user' = ... 相当の検索がインデックスに乗る。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
sqlite3 :memory: <<'SQL' CREATE TABLE events( id INTEGER PRIMARY KEY, body BLOB, user TEXT GENERATED ALWAYS AS (body ->> 'user') VIRTUAL ); CREATE INDEX idx_events_user ON events(user);
INSERT INTO events(body) VALUES (jsonb('{"user":"alice","action":"login"}')), (jsonb('{"user":"bob","action":"logout"}'));
-- インデックスが使われることを確認 EXPLAIN QUERY PLAN SELECT id FROM events WHERE user = 'alice'; SQL
SEARCH events USING INDEX idx_events_user (user=?) と出れば、生成列インデックスで検索できている。VIRTUAL はディスクに列を保存せず参照時に計算する指定で、インデックス自体は作られるため検索は速いままだ。
更新は jsonb_set で
部分更新も JSONB のまま行える。
1 2 3 4 5 6 7 8 9 10
sqlite3 :memory: <<'SQL' CREATE TABLE t(id INTEGER PRIMARY KEY, body BLOB); INSERT INTO t(body) VALUES (jsonb('{"user":"alice","score":42}'));
-- score を 100 に更新(結果も JSONB のまま保持) UPDATE t SET body = jsonb_set(body, '$.score', 100) WHERE id = 1;
-- 確認のためテキストに戻して表示 SELECT json(body) FROM t; -- {"user":"alice","score":100} SQL