thesoftwarehouse/copilot-collections/tsh-sql-and-database-understanding
Claude Code and agent skill by thesoftwarehouse/copilot-collections
- community
Checking that the SKILL.md file is still in the repository.
SQL writing and database engineering patterns, standards, and procedures. Use for designing database schemas, writing performant SQL queries, normalisation strategies, indexing, joins optimisation, locking mechanics, transactions, query debugging with EXPLAIN, and ORM integration. Applies to PostgreSQL, MySQL, MariaDB, SQL Server, and Oracle. Covers ORM usage with TypeORM, Prisma, Doctrine, Eloquent, Entity Framework, Hibernate, and GORM.
Install
npx vskill@latest install thesoftwarehouse/copilot-collections/tsh-sql-and-database-understandingSKILL.md
SQL & Database Engineering
This skill provides standards, patterns, and procedures for database schema design, writing performant SQL, query debugging, and database engineering best practices. It is database-engine-agnostic with notes on engine-specific behavior where critical.
Guiding Principles
| Principle | Application | |
|---|---|---|
| Data Integrity First | Enforce constraints at the database level (NOT NULL, UNIQUE, FK, CHECK). Never rely solely on application-level validation. | |
| Least Privilege | Database users/roles should have only the permissions they need. Application connections should never use the superuser account. | |
| Explicit Over Implicit | Always specify column lists in SELECT, INSERT, and JOIN clauses. Avoid SELECT * in production code. | |
| Measure Before Optimising | Use EXPLAIN ANALYZE to identify actual bottlenecks before adding indexes or restructuring queries. | |
| Schema as Code | All schema changes go through versioned migration files. Never modify production schemas manually. |
1. Database Schema Architecture Design
Naming Conventions
| Element | Convention | Example | |
|---|---|---|---|
| Tables | snake_case, plural nouns | order_items, user_addresses | |
| Columns | snake_case | first_name, created_at | |
| Primary keys | id (preferred) or <table_singular>_id | id, user_id | |
| Foreign keys | <referenced_table_singular>_id | user_id, order_id | |
| Indexes | idx_<table>_<columns> | idx_orders_user_id_status | |
| Unique constraints | uq_<table>_<columns> | uq_users_email | |
| Check constraints | chk_<table>_<description> | chk_orders_total_positive | |
| Junction/pivot tables | <table1>_<table2> (alphabetical) | products_tags, roles_users |
Standard Columns
Every table MUST include:
When soft deletes are required by business rules:
Primary Key Strategy
| Strategy | When to Use | Trade-offs | |
|---|---|---|---|
UUID v4 / UUID v7 | Distributed systems, microservices, public-facing IDs | Larger storage, random UUIDs cause index fragmentation (prefer UUID v7 for ordered inserts) | |
ULID | When you need sortable unique IDs with good index locality | Not natively supported in all databases | |
BIGSERIAL / IDENTITY | Single-database monoliths, internal IDs | Sequential, predictable (security concern if exposed), not portable across DB instances |
Rule: Never expose auto-increment IDs in public APIs. Use UUIDs or ULIDs for external identifiers.
Data Types — Choose Precisely
| Need | Use | Avoid | |
|---|---|---|---|
| Monetary values | NUMERIC(19,4) or DECIMAL(19,4) | FLOAT, DOUBLE (precision loss) | |
| Timestamps | TIMESTAMP WITH TIME ZONE | TIMESTAMP without timezone | |
| Boolean flags | BOOLEAN | TINYINT, CHAR(1) | |
| Short text (name, email) | VARCHAR(n) with appropriate limit | Unbounded TEXT for structured fields | |
| Long text (descriptions) | TEXT | VARCHAR(10000) | |
| Enums | VARCHAR with CHECK constraint or native ENUM | Magic integers | |
| JSON/semi-structured | JSONB (PostgreSQL) or JSON | Storing relational data as JSON | |
| IP addresses | INET (PostgreSQL) or VARCHAR(45) | VARCHAR(15) (IPv6 won't fit) |
Enum Handling
Prefer CHECK constraints over native ENUM types for portability and ease of migration:
Adding a new enum value is a simple ALTER TABLE ... DROP CONSTRAINT + ADD CONSTRAINT, no table rewrite required.
Schema Design Checklist
2. Normalisation Strategies
Normal Forms Reference
| Normal Form | Rule | Violation Example | Fix | |
|---|---|---|---|---|
| 1NF | Every column holds atomic (indivisible) values. No repeating groups. | tags: "php,sql,go" in a single column | Create a separate tags table with a junction table | |
| 2NF | 1NF + every non-key column depends on the entire primary key (relevant for composite keys) | order_items(order_id, product_id, product_name) — product_name depends only on product_id | Move product_name to the products table | |
| 3NF | 2NF + no transitive dependencies (non-key column depends on another non-key column) | employees(id, department_id, department_name) — department_name depends on department_id, not on id | Move department_name to a departments table | |
| BCNF | 3NF + every determinant is a candidate key | Rare in practice; address when composite keys create functional dependency issues | Decompose the table so every determinant is a key |
When to Use Each Level
Target 3NF by default. This eliminates redundancy while keeping the schema manageable.
Use 2NF only as an intermediate step when refactoring legacy schemas — never as a design target.
Use BCNF when you have composite primary keys with overlapping candidate keys (uncommon in application databases).
Practical Normalisation Example
Unnormalised (0NF):
1NF — Atomic values, no repeating groups:
2NF — Remove partial dependencies (product details depend on product, not order):
3NF — Remove transitive dependencies (customer depends on customer, not order):
Strategic Denormalisation
Denormalise only when you have measured performance data (via EXPLAIN ANALYZE) proving that joins are the bottleneck. Common valid cases:
| Pattern | When to Use | Example | |
|---|---|---|---|
| Materialised/cached columns | Frequently read aggregate that is expensive to compute on every query | orders.total_amount computed from order_items and stored on the order row | |
| Snapshot columns | Values that must be preserved at a point in time even if the source changes | order_items.unit_price (price at time of purchase, not current product price) | |
| Read-optimised views | Reporting or analytics queries that span many tables | Materialised views refreshed periodically | |
| Search/filter columns | Columns derived from related tables used heavily in WHERE clauses | orders.customer_country duplicated from customers.country for filtering |
Rules for denormalisation:
- Always document why the denormalisation exists (code comment + migration description).
- Ensure the denormalised data is kept in sync (application-level updates, triggers as last resort, or materialised view refresh).
- Treat denormalised data as a cache — the normalised source remains the source of truth.
3. Relationships & Foreign Keys
Relationship Types
One-to-Many (most common):
Many-to-Many (junction table):
One-to-One:
ON DELETE / ON UPDATE Actions
| Action | Meaning | When to Use | |
|---|---|---|---|
RESTRICT (default) | Prevent deletion if referenced rows exist | Default for most business entities (prevent accidental data loss) | |
CASCADE | Delete/update child rows when parent is deleted/updated | Junction tables, dependent child records that have no meaning without parent | |
SET NULL | Set FK to NULL when parent is deleted | Optional relationships where the child can exist independently | |
SET DEFAULT | Set FK to its default value | Rare — use when a fallback reference makes business sense | |
NO ACTION | Same as RESTRICT but checked at end of transaction | When deferred constraint checking is needed |
Rule: Default to RESTRICT. Use CASCADE only on junction tables and tightly coupled child tables. Always explicitly specify the action — never rely on implicit defaults.
Self-Referencing Relationships
For deep hierarchies (trees), consider:
- Adjacency list (above) — simple, good for shallow trees
- Materialised path —
path VARCHAR(500)storing/1/5/12/— good for read-heavy trees - Nested sets — complex to maintain but fast for subtree queries
- Closure table — separate table storing all ancestor-descendant pairs — balanced read/write performance
4. Indexes
Index Types
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryChecking.github/skills/tsh-sql-and-database-understanding/SKILL.md
Popularity
- Repository stars
- 266
- Repository forks
- 18
- 7-day trend score
- 28
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/thesoftwarehouse/copilot-collections/tsh-sql-and-database-understanding)