# PostgreSQL — Patterns e Anti-patterns > Conexão | Schemas | JSONB | Junções | Window Functions | Migrações | EXPLAIN ## Comandos Rápidos ```bash psql "postgresql://user:pass@host:5432/db?sslmode=require" psql -h localhost -U user -d db -c "SELECT NOW();" psql -h host -U user -d db -f migration.sql ``` ## Schema Design ```sql -- UUIDs para PK distribuído-friendly (ativar extensão primeiro) CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), email TEXT NOT NULL UNIQUE, name TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'user' CHECK(role IN ('user','admin','moderator')), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- Auto-update updated_at CREATE OR REPLACE FUNCTION update_modified_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER update_users_modtime BEFORE UPDATE ON users FOR EACH ROW EXECUTE FUNCTION update_modified_column(); -- Enum CREATE TYPE order_status AS ENUM ('pending','paid','shipped','cancelled'); ``` ## JSONB Ops ```sql INSERT INTO orders (metadata) VALUES ('{"source":"web","items":[{"sku":"A1","qty":2}]}'::jsonb); SELECT * FROM orders WHERE metadata->>'source' = 'web'; SELECT * FROM orders WHERE metadata->'items' @> '[{"sku":"A1"}]'::jsonb; -- @> contains UPDATE orders SET metadata = jsonb_set(metadata, '{source}', '"mobile"') WHERE id = '...'; SELECT metadata->>'coupon' AS coupon, COUNT(*) FROM orders GROUP BY 1; ``` ## Indexes ```sql CREATE UNIQUE INDEX idx_users_email ON users(email); -- único CREATE INDEX idx_orders_user_created ON orders(user_id, created_at); -- composto CREATE INDEX idx_orders_active ON orders(user_id, created_at) -- parcial WHERE status NOT IN ('delivered','cancelled'); CREATE INDEX idx_orders_metadata ON orders USING GIN(metadata); -- GIN para JSONB ``` ## Window Functions ```sql SELECT date, revenue, SUM(revenue) OVER (ORDER BY date) AS cumulative_revenue, -- running total AVG(revenue) OVER (PARTITION BY region) AS avg_by_region, RANK() OVER (ORDER BY revenue DESC) AS ranking FROM daily_sales; ``` ## JOINS Rápidos ```sql -- LEFT JOIN pega todos os usuários, mesmo sem pedidos SELECT u.name, COUNT(o.id) AS order_count, COALESCE(SUM(o.total), 0) AS total_spent FROM users u LEFT JOIN orders o ON o.user_id = u.id GROUP BY u.id, u.name HAVING COUNT(o.id) > 5 ORDER BY total_spent DESC; ``` ## CTEs (WITH) ```sql WITH active_users AS ( SELECT id, name FROM users WHERE active = true ) SELECT au.name, COUNT(o.id) AS orders FROM active_users au LEFT JOIN orders o ON o.user_id = au.id GROUP BY au.name; ``` ## EXPLAIN ANALYZE (antes de otimizar) ```bash EXPLAIN ANALYZE SELECT ... -- SEMPRE antes de criar um índice ``` ## Migrations — Padrão ```bash # Criar timestamped psql -h host -U user -d db -f migrations/2025-06-19_add_field.sql ``` ## Checklist de Otimização - [ ] `EXPLAIN ANALYZE` antes e depois de cada mudança - [ ] Medir tempo de resposta alvo antes de otimizar - [ ] Evitar `SELECT *` em produção - [ ] Criar índices para colunas em `WHERE`, `JOIN`, `ORDER BY` - [ ] Índice parcial para subconjuntos pequenos - [ ] Partial/Paginado para tabelas grandes