sql
296 lines · 2 tabs
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;
-- JSON aggregation
SELECT
profile ->> 'city' AS city,
jsonb_agg(
jsonb_build_object(
'username', username,
'age', profile ->> 'age'
)
) AS users
FROM users
GROUP BY profile ->> 'city';
-- JSON object aggregation
SELECT
jsonb_object_agg(
username,
profile -> 'age'
) AS user_ages
FROM users;
-- Pretty print JSON
SELECT jsonb_pretty(profile) FROM users LIMIT 1;
-- Get all keys
SELECT DISTINCT jsonb_object_keys(profile) AS key
FROM users;
-- Get all values
SELECT
username,
key,
value
FROM users,
jsonb_each(profile)
WHERE key IN ('age', 'city');
-- Expand JSON to rows
SELECT
u.username,
e.key,
e.value
FROM users u
CROSS JOIN LATERAL jsonb_each_text(u.profile) e;
-- Array to rows
SELECT
u.username,
skill
FROM users u
CROSS JOIN LATERAL jsonb_array_elements_text(u.profile -> 'skills') skill;
-- Merge JSONB objects
SELECT
profile || preferences AS merged
FROM users
LIMIT 1;
-- Deep merge
SELECT
jsonb_set(
profile,
'{contact}',
COALESCE(profile -> 'contact', '{}'::jsonb) ||
'{"twitter": "@user"}'::jsonb
) AS updated_profile
FROM users;
-- Conditional update
UPDATE users
SET preferences = CASE
WHEN preferences ? 'theme'
THEN jsonb_set(preferences, '{theme}', '"light"')
ELSE preferences || '{"theme": "light"}'
END;
-- Type checking
SELECT
username,
jsonb_typeof(profile -> 'age') AS age_type,
jsonb_typeof(profile -> 'skills') AS skills_type
FROM users;
-- Validate JSON structure
CREATE OR REPLACE FUNCTION validate_user_profile(data JSONB)
RETURNS BOOLEAN AS $$
BEGIN
RETURN (
data ? 'age' AND
jsonb_typeof(data -> 'age') = 'number' AND
(data ->> 'age')::INT BETWEEN 0 AND 150 AND
data ? 'city' AND
jsonb_typeof(data -> 'city') = 'string'
);
END;
$$ LANGUAGE plpgsql IMMUTABLE;
-- Add constraint
ALTER TABLE users
ADD CONSTRAINT valid_profile
CHECK (validate_user_profile(profile));
-- Full-text search on JSON
CREATE INDEX idx_users_profile_fts
ON users
USING GIN (to_tsvector('english', profile::text));
SELECT username
FROM users
WHERE to_tsvector('english', profile::text) @@
to_tsquery('english', 'Python & SQL');
-- Event store pattern
CREATE TABLE events (
id SERIAL PRIMARY KEY,
event_type VARCHAR(50) NOT NULL,
payload JSONB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_events_type ON events (event_type);
CREATE INDEX idx_events_payload_gin ON events USING GIN (payload);
INSERT INTO events (event_type, payload) VALUES
(
'user_registered',
'{"user_id": 123, "email": "user@example.com", "source": "web"}'
),
(
'order_placed',
'{"order_id": 456, "user_id": 123, "total": 99.99, "items": [{"id": 1, "qty": 2}]}'
);
-- Query events
SELECT *
FROM events
WHERE event_type = 'order_placed'
AND (payload ->> 'total')::DECIMAL > 50;
-- Schemaless tables with JSONB
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
type VARCHAR(50) NOT NULL,
data JSONB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_documents_type ON documents (type);
CREATE INDEX idx_documents_data_gin ON documents USING GIN (data);
-- Different document types in same table
INSERT INTO documents (type, data) VALUES
('product', '{"name": "Laptop", "price": 999, "specs": {"cpu": "i7", "ram": "16GB"}}'),
('article', '{"title": "DB Guide", "author": "Maria", "content": "...", "tags": ["db", "sql"]}');
-- Type-specific queries
SELECT data ->> 'name' AS product_name
FROM documents
WHERE type = 'product'
AND (data ->> 'price')::DECIMAL < 1000;
SELECT data ->> 'title' AS article_title
FROM documents
WHERE type = 'article'
AND data -> 'tags' @> '["sql"]';
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.