sql 296 lines · 2 tabs

Working with JSON and JSONB in PostgreSQL

Maria Garcia Feb 2026
2 tabs
-- Create table with JSONB column
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  username VARCHAR(50),
  profile JSONB,
  preferences JSONB,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Insert JSON data
INSERT INTO users (username, profile, preferences) VALUES
(
  'alice',
  '{"age": 25, "city": "New York", "skills": ["SQL", "Python", "JavaScript"]}',
  '{"theme": "dark", "notifications": true, "language": "en"}'
);

-- Insert with jsonb_build_object
INSERT INTO users (username, profile) VALUES
(
  'bob',
  jsonb_build_object(
    'age', 30,
    'city', 'San Francisco',
    'skills', jsonb_build_array('Ruby', 'Go'),
    'contact', jsonb_build_object('email', 'bob@example.com', 'phone', '555-0100')
  )
);

-- Extract JSON field (returns JSON)
SELECT
  username,
  profile -> 'city' AS city_json,
  profile ->> 'city' AS city_text
FROM users;

-- -> returns JSON, ->> returns text

-- Extract nested field
SELECT
  username,
  profile -> 'contact' ->> 'email' AS email,
  profile #> '{contact,email}' AS email_path
FROM users;

-- Filter by JSON field
SELECT username
FROM users
WHERE profile ->> 'city' = 'New York';

-- Check key existence
SELECT username
FROM users
WHERE profile ? 'age';  -- Has 'age' key

-- Check multiple keys
SELECT username
FROM users
WHERE profile ?& ARRAY['age', 'city'];  -- Has all keys

SELECT username
FROM users
WHERE profile ?| ARRAY['age', 'location'];  -- Has any key

-- Containment (@> contains, <@ contained by)
SELECT username
FROM users
WHERE profile @> '{"city": "New York"}';

-- Find users with specific skill
SELECT username
FROM users
WHERE profile @> '{"skills": ["SQL"]}';

-- JSON array operations
SELECT
  username,
  jsonb_array_length(profile -> 'skills') AS skill_count,
  jsonb_array_elements_text(profile -> 'skills') AS skill
FROM users;

-- Update JSON field
UPDATE users
SET profile = profile || '{"verified": true}'
WHERE username = 'alice';

-- Update nested field
UPDATE users
SET profile = jsonb_set(
  profile,
  '{contact,phone}',
  '"555-0200"'
)
WHERE username = 'bob';

-- Remove field
UPDATE users
SET profile = profile - 'temporary_field'
WHERE id = 1;

-- Remove nested field
UPDATE users
SET profile = profile #- '{contact,phone}'
WHERE id = 2;

-- GIN index for JSONB
CREATE INDEX idx_users_profile_gin ON users USING GIN (profile);

-- Now containment queries use index
EXPLAIN ANALYZE
SELECT * FROM users
WHERE profile @> '{"city": "New York"}';

-- Index specific JSON path
CREATE INDEX idx_users_profile_city
  ON users ((profile ->> 'city'));

-- Expression index on nested field
CREATE INDEX idx_users_email
  ON users ((profile -> 'contact' ->> 'email'));

-- JSON path queries (PostgreSQL 12+)
SELECT username, profile
FROM users
WHERE profile @? '$.skills[*] ? (@ == "SQL")';

-- jsonb_path_query
SELECT
  username,
  jsonb_path_query(profile, '$.skills[*]') AS skill
FROM users;
2 files · sql Explain with highlit

JSON and JSONB store semi-structured data. JSONB is binary format—faster, indexable. I use JSONB for flexible schemas, API responses, configuration. JSON operators extract values, filter documents. GIN indexes enable fast JSONB queries. Containment operators check for key existence. Path expressions traverse nested structures. JSON aggregation builds complex documents. Understanding JSONB vs JSON trade-offs guides choice. JSONB supports indexing, JSON preserves formatting. Proper use of JSONB reduces schema changes. Essential for modern web applications, analytics, event logging. PostgreSQL JSONB rivals NoSQL databases for document storage.


Related snips

sql
-- Simple function
CREATE OR REPLACE FUNCTION get_full_name(
  first_name VARCHAR,
  last_name VARCHAR
)
RETURNS VARCHAR AS $$

Stored procedures and functions in PostgreSQL

postgresql stored-procedures functions
by Maria Garcia 2 tabs
ruby
class AddSettingsToAccounts < ActiveRecord::Migration[7.1]
  disable_ddl_transaction!

  def change
    add_column :accounts, :settings, :jsonb, null: false, default: {}

Postgres JSONB Partial Index for Feature Flags

rails postgres jsonb
by codesnips 3 tabs
sql
-- EXPLAIN ANALYZE (actual execution statistics)
EXPLAIN ANALYZE
SELECT u.username, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at >= '2024-01-01'

Advanced query optimization techniques

database optimization query-performance
by Maria Garcia 2 tabs
sql
-- Publisher: Send notification
NOTIFY new_order, 'Order #12345 created';

-- Subscriber: Listen for notifications
LISTEN new_order;

PostgreSQL LISTEN/NOTIFY for pub-sub messaging

postgresql listen-notify pub-sub
by Maria Garcia 2 tabs
sql
-- Logical backup with pg_dump
-- Single database
-- pg_dump -h localhost -U postgres -d mydb -F c -f mydb_backup.dump

-- All databases
-- pg_dumpall -h localhost -U postgres -f all_databases.sql

Database backup and recovery strategies

database backup recovery
by Maria Garcia 2 tabs
sql
-- Prepared statements basics
-- PostgreSQL syntax
PREPARE get_user (INT) AS
SELECT id, username, email
FROM users
WHERE id = $1;

Query plan caching and prepared statements

postgresql performance query-plans
by Maria Garcia 2 tabs

Share this code

Here's the card — post it anywhere.

Working with JSON and JSONB in PostgreSQL — share card
Link copied