Sql expert
Skill yigityildiz0/universal-ai-skill-library/skills/common/sql-expert
531 searchable AI Agent Skills for Claude Code, OpenAI Codex, and OpenCode — EN/TR catalog, platform and risk notes, direct ZIPs, and curated bundles.
npx -y skills add yigityildiz0/universal-ai-skill-library --skill sql-expertAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
3 things to look at
- 20 days oldThe repository was created 20 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Deep SQL expertise for query optimization and database design. Use when writing complex queries, optimizing slow queries, designing schemas, understanding.
SKILL.md
12.7 KB, ~3.2k tokens by cl100k_base, as published. Nobody here has run it
SQL Expert
Specialized expertise in SQL and relational database design, providing deep guidance on query optimization, indexing strategies, schema design, and database-specific features across PostgreSQL, MySQL, and SQL Server.
When to Use This Skill
Use this skill for:
- Writing complex SQL queries
- Optimizing slow queries
- Designing database schemas
- Understanding execution plans
- Creating effective indexes
- Database-specific features
- Performance tuning
Trigger phrases: "SQL", "database query", "optimize query", "index", "execution plan", "schema design", "PostgreSQL", "MySQL", "SQL Server"
What This Skill Does
Provides SQL expertise including:
- Query Optimization: Rewriting queries for performance
- Index Strategy: Designing effective indexes
- Schema Design: Normalization, data modeling
- Execution Plans: Reading and understanding plans
- Database Features: DB-specific capabilities
- Performance Tuning: Identifying and fixing bottlenecks
Instructions
Step 1: Analyze Query Performance
Understanding Execution Plans:
-- PostgreSQL
EXPLAIN ANALYZE
SELECT u.name, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE u.created_at > '2024-01-01'
GROUP BY u.id, u.name;
-- MySQL
EXPLAIN FORMAT=JSON
SELECT ...
-- SQL Server
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
SELECT ...
Reading Execution Plans:
| Operation | Good Sign | Bad Sign |
|---|---|---|
| Index Scan | Using covering index | Full table scan when index exists |
| Seq Scan | Small table | Large table with selective WHERE |
| Nested Loop | Small inner table | Large tables on both sides |
| Hash Join | Large tables, equality join | Not enough memory |
| Sort | Indexed order | Large sort without index |
Step 2: Optimize Common Query Patterns
**Avoid SELECT ***:
-- Bad: Fetches all columns
SELECT * FROM users WHERE status = 'active';
-- Good: Only needed columns
SELECT id, name, email FROM users WHERE status = 'active';
Use EXISTS Instead of IN for Subqueries:
-- Potentially slow with large subquery result
SELECT * FROM products
WHERE category_id IN (SELECT id FROM categories WHERE active = true);
-- Better: EXISTS stops at first match
SELECT * FROM products p
WHERE EXISTS (
SELECT 1 FROM categories c
WHERE c.id = p.category_id AND c.active = true
);
Optimize JOINs:
-- Bad: Joining on non-indexed columns
SELECT o.*, p.name
FROM orders o
JOIN products p ON p.sku = o.product_sku;
-- Good: Join on indexed foreign key
SELECT o.*, p.name
FROM orders o
JOIN products p ON p.id = o.product_id;
-- Use appropriate join type
-- INNER JOIN: Need matches in both tables
-- LEFT JOIN: Need all from left, matches from right
-- Don't use LEFT JOIN if you actually need INNER JOIN
Avoid Functions on Indexed Columns:
-- Bad: Function prevents index usage
SELECT * FROM users WHERE YEAR(created_at) = 2024;
-- Good: Range condition uses index
SELECT * FROM users
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
-- Bad: Function on column
SELECT * FROM users WHERE LOWER(email) = '[email protected]';
-- Good: Use functional index (PostgreSQL) or store lowercase
CREATE INDEX idx_users_email_lower ON users (LOWER(email));
-- Or store email in lowercase
Step 3: Design Effective Indexes
Index Types:
-- B-tree (default): Equality, range queries
CREATE INDEX idx_users_email ON users(email);
-- Composite: Multiple columns (order matters!)
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
-- Good for: WHERE user_id = ? AND created_at > ?
-- Good for: WHERE user_id = ?
-- Bad for: WHERE created_at > ? (user_id not in query)
-- Partial index: Subset of rows
CREATE INDEX idx_active_users ON users(email) WHERE status = 'active';
-- Covering index: Includes all needed columns
CREATE INDEX idx_orders_covering ON orders(user_id, created_at)
INCLUDE (total, status);
-- GIN/Full-text: Text search, arrays, JSONB
CREATE INDEX idx_products_tags ON products USING GIN(tags);
CREATE INDEX idx_products_search ON products USING GIN(to_tsvector('english', name || ' ' || description));
Index Strategy Guidelines:
| Query Pattern | Index Strategy |
|---|---|
WHERE col = ? | Single column B-tree |
WHERE col1 = ? AND col2 = ? | Composite (col1, col2) |
WHERE col1 = ? ORDER BY col2 | Composite (col1, col2) |
WHERE col IN (?, ?, ?) | Single column B-tree |
WHERE col LIKE 'prefix%' | B-tree (prefix only) |
WHERE col LIKE '%substring%' | GIN trigram |
WHERE col @> '{"key": "value"}' | GIN on JSONB |
Step 4: Optimize Specific Patterns
Pagination:
-- Bad: OFFSET is slow for large offsets
SELECT * FROM products ORDER BY id LIMIT 20 OFFSET 10000;
-- Good: Keyset pagination (cursor-based)
SELECT * FROM products
WHERE id > 10000
ORDER BY id
LIMIT 20;
-- For complex ordering, use indexed columns
SELECT * FROM products
WHERE (created_at, id) > ('2024-01-15', 5000)
ORDER BY created_at, id
LIMIT 20;
Aggregations:
-- Slow: Aggregating large table
SELECT category_id, COUNT(*) FROM products GROUP BY category_id;
-- Better: Maintain materialized view or counter table
CREATE MATERIALIZED VIEW category_counts AS
SELECT category_id, COUNT(*) as product_count
FROM products GROUP BY category_id;
-- Or use approximate counts
SELECT reltuples::bigint AS estimate
FROM pg_class WHERE relname = 'products';
Batch Operations:
-- Bad: Many individual inserts
INSERT INTO logs (message) VALUES ('log1');
INSERT INTO logs (message) VALUES ('log2');
-- ... thousands more
-- Good: Batch insert
INSERT INTO logs (message) VALUES
('log1'), ('log2'), ('log3'), ... ;
-- Better: Use COPY (PostgreSQL) or LOAD DATA (MySQL)
COPY logs (message) FROM '/path/to/data.csv' CSV;
-- For updates, batch with CTEs
WITH batch AS (
SELECT id FROM products WHERE needs_update = true LIMIT 1000
)
UPDATE products SET updated_at = NOW()
WHERE id IN (SELECT id FROM batch);
Step 5: Schema Design Best Practices
Normalization Levels:
-- 1NF: Atomic values, no repeating groups
-- Bad: Repeating columns
CREATE TABLE orders (
id INT,
product1_id INT, product1_qty INT,
product2_id INT, product2_qty INT
);
-- Good: Separate table
CREATE TABLE orders (id INT PRIMARY KEY);
CREATE TABLE order_items (
order_id INT REFERENCES orders(id),
product_id INT,
quantity INT
);
-- 3NF: No transitive dependencies
-- Bad: city depends on zip, not directly on id
CREATE TABLE customers (
id INT,
name VARCHAR(100),
zip VARCHAR(10),
city VARCHAR(100) -- Derived from zip
);
-- Good: Separate lookup
CREATE TABLE zipcodes (zip VARCHAR(10) PRIMARY KEY, city VARCHAR(100));
CREATE TABLE customers (
id INT,
name VARCHAR(100),
zip VARCHAR(10) REFERENCES zipcodes(zip)
);
Strategic Denormalization:
-- When to denormalize:
-- 1. Read-heavy with complex joins
-- 2. Aggregations computed frequently
-- 3. Performance critical paths
-- Example: Storing computed total
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT,
total DECIMAL(10,2), -- Denormalized sum of items
created_at TIMESTAMP
);
-- Keep consistent with trigger
CREATE FUNCTION update_order_total() RETURNS TRIGGER AS $$
BEGIN
UPDATE orders SET total = (
SELECT SUM(quantity * price)
FROM order_items WHERE order_id = NEW.order_id
) WHERE id = NEW.order_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Step 6: Database-Specific Features
PostgreSQL:
-- JSONB for semi-structured data
CREATE TABLE events (
id SERIAL PRIMARY KEY,
data JSONB NOT NULL
);
CREATE INDEX idx_events_data ON events USING GIN(data);
-- Query JSONB
SELECT * FROM events WHERE data @> '{"type": "click"}';
SELECT data->>'user_id' as user_id FROM events;
-- Window functions
SELECT
name,
department,
salary,
AVG(salary) OVER (PARTITION BY department) as dept_avg,
RANK() OVER (ORDER BY salary DESC) as salary_rank
FROM employees;
-- CTEs and recursive queries
WITH RECURSIVE org_tree AS (
SELECT id, name, manager_id, 0 as depth
FROM employees WHERE manager_id IS NULL
UNION ALL
SELECT e.id, e.name, e.manager_id, t.depth + 1
FROM employees e
JOIN org_tree t ON e.manager_id = t.id
)
SELECT * FROM org_tree;
-- Full-text search
SELECT * FROM products
WHERE to_tsvector('english', name || ' ' || description)
@@ to_tsquery('english', 'wireless & headphones');
MySQL:
-- Generated columns
CREATE TABLE products (
id INT PRIMARY KEY,
price DECIMAL(10,2),
tax_rate DECIMAL(4,2),
total_price DECIMAL(10,2) GENERATED ALWAYS AS (price * (1 + tax_rate)) STORED
);
-- JSON functions
SELECT
id,
JSON_EXTRACT(metadata, '$.category') as category
FROM products
WHERE JSON_CONTAINS(metadata, '"electronics"', '$.tags');
-- Full-text search
ALTER TABLE products ADD FULLTEXT(name, description);
SELECT * FROM products
WHERE MATCH(name, description) AGAINST('wireless headphones' IN NATURAL LANGUAGE MODE);
SQL Server:
-- Window functions with ROWS/RANGE
SELECT
date,
amount,
SUM(amount) OVER (
ORDER BY date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) as rolling_7day
FROM transactions;
-- JSON support
SELECT
id,
JSON_VALUE(data, '$.name') as name,
JSON_QUERY(data, '$.addresses') as addresses
FROM customers
WHERE JSON_VALUE(data, '$.status') = 'active';
-- Temporal tables (system-versioned)
CREATE TABLE products (
id INT PRIMARY KEY,
name NVARCHAR(100),
price DECIMAL(10,2),
valid_from DATETIME2 GENERATED ALWAYS AS ROW START,
valid_to DATETIME2 GENERATED ALWAYS AS ROW END,
PERIOD FOR SYSTEM_TIME (valid_from, valid_to)
) WITH (SYSTEM_VERSIONING = ON);
-- Query historical data
SELECT * FROM products FOR SYSTEM_TIME AS OF '2024-01-01';
Best Practices
- Profile before optimizing - Use EXPLAIN ANALYZE
- Index selectively - Indexes have write overhead
- **Avoid SELECT *** - Specify needed columns
- Use appropriate data types - Smaller is faster
- Batch large operations - Don't update 1M rows at once
- Monitor slow queries - Log and review regularly
- Test with production-like data - Volume matters
- Keep statistics updated - ANALYZE/UPDATE STATISTICS
Common Patterns
Pattern 1: Upsert (Insert or Update)
-- PostgreSQL
INSERT INTO products (id, name, price)
VALUES (1, 'Widget', 9.99)
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name, price = EXCLUDED.price;
-- MySQL
INSERT INTO products (id, name, price)
VALUES (1, 'Widget', 9.99)
ON DUPLICATE KEY UPDATE
name = VALUES(name), price = VALUES(price);
-- SQL Server
MERGE INTO products AS target
USING (VALUES (1, 'Widget', 9.99)) AS source (id, name, price)
ON target.id = source.id
WHEN MATCHED THEN UPDATE SET name = source.name, price = source.price
WHEN NOT MATCHED THEN INSERT (id, name, price) VALUES (source.id, source.name, source.price);
Pattern 2: Running Totals
SELECT
date,
amount,
SUM(amount) OVER (ORDER BY date) as running_total
FROM transactions;
Pattern 3: Gap and Island Detection
-- Find consecutive date ranges
WITH numbered AS (
SELECT
date,
date - (ROW_NUMBER() OVER (ORDER BY date) * INTERVAL '1 day') as grp
FROM events
)
SELECT
MIN(date) as start_date,
MAX(date) as end_date,
COUNT(*) as consecutive_days
FROM numbered
GROUP BY grp
ORDER BY start_date;
Quality Checklist
- Query uses indexes effectively (EXPLAIN verified)
- No SELECT * in production queries
- JOINs are on indexed columns
- No functions on indexed columns in WHERE
- Large operations are batched
- Pagination uses keyset method
- Appropriate indexes created
- Query tested with production data volume
Related Skills
performance-review- Database performance assessmentcode-quality- SQL code standardssecurity-review- SQL injection preventionterraform-specialist- Database infrastructure
Version: 1.0.0 Last Updated: January 2026 Based on: Use the Index, Luke; awesome-claude-code-subagents patterns
Iterative Refinement Strategy
This skill is optimized for an iterative approach:
- Execute: Perform the core steps defined above.
- Review: Critically analyze the output (coverage, quality, completeness).
- Refine: If targets aren't met, repeat the specific implementation steps with improved context.
- Loop: Continue until the definition of done is satisfied.
What ships with it: 1 file
266 B alongside SKILL.md
agents/
- openai.yaml266 B