Loading learning content...
Names matter. In database design, the names you choose for entities, attributes, and relationships determine whether your model is a clear communication tool or an opaque puzzle that only its creator can decipher.
Consider two approaches to the same model:
Poor naming:
tbl_c(c_id, nm, addr1, addr2, dt, stat)
tbl_o(o_id, c_fk, dt1, dt2, amt, st)
Clear naming:
Customer(customer_id, full_name, billing_address, shipping_address, registration_date, status)
Order(order_id, customer_id, order_date, ship_date, total_amount, order_status)
Both models contain the same information, yet one is immediately understandable while the other requires documentation lookup for every column. In a model with hundreds of entities and thousands of attributes, poor naming creates significant ongoing costs—in development time, bug rates, onboarding effort, and maintenance burden.
This page establishes comprehensive principles and practices for naming database elements. These aren't arbitrary style preferences; they're battle-tested conventions that improve clarity, reduce errors, and make databases more maintainable over their multi-year lifespans.
By the end of this page, you will understand why naming matters, master entity naming conventions, learn attribute naming best practices, establish relationship naming standards, understand how naming differs across model levels, and create consistent naming policies for your projects.
Database names serve multiple stakeholders over long timeframes. A table named in 2023 might still be queried daily in 2033, by developers who never met the original designer. Names are the primary documentation embedded in the schema itself.
Stakeholders Who Read Database Names:
Each stakeholder benefits from clear, consistent naming that reveals meaning without requiring external documentation.
The Costs of Poor Naming:
Immediate Costs:
Long-Term Costs:
A new developer looking at your schema should be able to guess what any table or column contains within 30 seconds, without consulting documentation. If they can't, your naming needs improvement. Good names are their own documentation.
Entity names (which become table names) follow specific conventions that enhance clarity and consistency.
1. Singular vs. Plural:
The most debated naming decision. Both have valid arguments:
Singular (Recommended):
Plural:
Choose one and be consistent. Most data modeling standards recommend singular.
2. Case Convention:
PascalCase:
snake_case:
UPPERCASE:
| Do | Don't | Reason |
|---|---|---|
| Customer | tbl_customer | Avoid physical prefixes in logical names |
| OrderLineItem | OrderDetails | 'Details' is vague; be specific about what it contains |
| EmployeeDepartment (junction) | EmpDept | Avoid abbreviations; full words are clearer |
| SalesOrder | Order | Distinguish from PurchaseOrder if both exist |
| CustomerAddress | CustAddr1 | Full words; avoid numbered suffixes |
| InvoicePayment (junction) | InvPmt | Readable junction table names |
| User or SystemUser | U or USR | Never single letters or extreme abbreviations |
3. Meaningful and Specific Names:
Avoid generic names that could mean anything:
# Bad - too generic
Item, Record, Entity, Object, Data, Info
# Good - specific
Product, TransactionRecord, CustomerEntity, DataExportLog
4. Domain Terminology:
Use terms from the business domain:
5. Avoiding Reserved Words:
Don't name entities with SQL reserved words:
# Problematic names (reserved words)
Order, User, Table, Index, Date, Key, Select, Group
# Solutions
SalesOrder, SystemUser, LookupTable, SearchIndex, EventDate, ApiKey, SelectOption, ProductGroup
Prefixing or making more specific avoids reservation conflicts.
Prefixes like tbl_, t_, or _table are vestiges of older practices that added no value. 'Customer' is clearly a table from context. Technical prefixes clutter SQL, obscure meaning, and create unnecessary typing. Keep names purely semantic.
Attribute names (column names) require careful attention because they appear in every query, every report, and every line of data access code.
1. Descriptive and Complete:
Attributes should fully describe what they contain:
-- Bad
amt, dt, nm, addr, stat
-- Good
order_total_amount, order_placed_date, customer_full_name, shipping_address, order_status
2. Case Convention (Consistent with Entities):
Use the same case convention as entity names:
-- snake_case (recommended for SQL)
customer_id, first_name, last_name, email_address, registration_date
-- camelCase (common in some ORMs)
customerId, firstName, lastName, emailAddress, registrationDate
3. Primary Key Naming:
Several approaches exist:
[entity]_id:
customer_id, order_id, product_id
id alone:
Customer.id, Order.id, Product.id
pk_[entity]:
pk_customer, pk_order
4. Foreign Key Naming:
Standard approach: Match the referenced table's primary key name:
Order.customer_id → Customer.customer_id
OrderItem.product_id → Product.product_id
With role clarification: When multiple FKs reference the same table:
Flight.departure_airport_id → Airport.airport_id
Flight.arrival_airport_id → Airport.airport_id
Employee.manager_employee_id → Employee.employee_id
Employee.mentor_employee_id → Employee.employee_id
5. Boolean Attribute Naming:
Boolean attributes should read naturally:
-- Good: reads as question with yes/no answer
is_active, is_verified, has_subscription, can_login, allows_notifications
-- Poor: unclear intention
active, verified, subscription, login, notifications
6. Date and Time Naming:
Include type and context:
-- Include what kind of date/time
created_at -- timestamp of creation
updated_at -- timestamp of last update
order_placed_date -- date order was placed
subscription_start_date
contract_end_date
delivery_estimated_at -- timestamp with time component
| Type | Pattern | Examples |
|---|---|---|
| Primary Key | [entity]_id | customer_id, order_id, product_id |
| Foreign Key | [role_][entity]_id | customer_id, manager_employee_id |
| Boolean | is_[state] / has_[thing] / can_[action] | is_active, has_premium, can_edit |
| Date (date only) | [noun]_date | birth_date, hire_date, expiry_date |
| Timestamp | [noun]_at / [verb]_at | created_at, updated_at, deleted_at |
| Amount/Money | [context]_amount | order_total_amount, refund_amount |
| Count | [noun]_count | item_count, attempt_count, view_count |
| Code/Enum | [noun]_code / [noun]_type | status_code, payment_type, country_code |
| Percentage | [noun]_percent / [noun]_rate | discount_percent, tax_rate |
Include context when the attribute could be ambiguous: 'date' → 'order_date' or 'ship_date'; 'amount' → 'subtotal_amount' or 'tax_amount'; 'name' → 'product_name' or 'category_name'. Within a specific table, the table name provides context, but in query results and exports, contextual naming maintains clarity.
Relationships connect entities, and their names should describe the connection meaningfully.
1. Verb-Based Relationship Names:
Relationships represent actions or associations. Name them with verbs:
Customer --places--> Order
Employee --works_in--> Department
Student --enrolls_in--> Course
Doctor --treats--> Patient
Product --belongs_to--> Category
The relationship name should read naturally in a sentence:
2. Directional Reading:
Relationships are typically read in one direction:
Customer (1) --places--> (N) Order
"One customer places many orders"
Order (N) <--placed_by-- (1) Customer
"Many orders are placed by one customer"
You may label both directions or choose one based on which is more natural.
3. Junction Table Naming:
For many-to-many relationships, junction tables need clear names:
Option A — Combined entity names:
StudentCourse (or student_course)
UserRole (or user_role)
ProductCategory (or product_category)
Option B — Semantic name (preferred when applicable):
Enrollment (student-course relationship with attributes like grade)
EmployeeSkill (employee-skill with proficiency level)
OrderItem (order-product with quantity and price)
Semantic names make more sense when the junction has its own attributes—it's essentially a reified relationship.
4. Role Names in Self-Referential Relationships:
When an entity relates to itself, role names clarify participation:
Employee (as manager) --manages--> Employee (as subordinate)
Person (as parent) --parent_of--> Person (as child)
Component (as assembly) --contains--> Component (as part)
In implementation:
CREATE TABLE Employee (
employee_id INT PRIMARY KEY,
name VARCHAR(100),
manager_employee_id INT REFERENCES Employee(employee_id),
mentor_employee_id INT REFERENCES Employee(employee_id)
);
5. Constraint Naming:
Name constraints explicitly for easier maintenance:
-- Primary Keys
CONSTRAINT pk_customer PRIMARY KEY (customer_id)
CONSTRAINT pk_order PRIMARY KEY (order_id)
-- Foreign Keys
CONSTRAINT fk_order_customer FOREIGN KEY (customer_id) REFERENCES Customer(customer_id)
-- Unique Constraints
CONSTRAINT uq_customer_email UNIQUE (email)
-- Check Constraints
CONSTRAINT chk_order_total_positive CHECK (total_amount >= 0)
Naming conventions:
Read your relationship aloud in a sentence. 'A Customer places an Order' flows naturally. 'A Customer order_links an Order' does not. If the name doesn't fit a natural English sentence, reconsider it. Domain experts should understand the relationship from its name alone.
Naming conventions may differ across model levels, though consistency within each level is essential.
Conceptual Level (ER Model):
Focused on business understanding:
Example:
Entities: Customer, Product, Order, Order Line Item
Attributes: Full Name, Email Address, Order Date, Unit Price
Relationships: places, contains, belongs to
Logical Level (Relational Schema):
Preparing for implementation:
Example:
Tables: Customer, Product, Order, OrderLineItem (or order_line_item)
Columns: full_name, email_address, order_date, unit_price, customer_id
Constraints: pk_customer, fk_order_customer
Physical Level (Database Implementation):
Platform-specific considerations:
Example (Oracle):
Tables: CUSTOMER, PRODUCT, SALES_ORDER
Columns: FULL_NAME, EMAIL_ADDRESS, ORDER_ID
Indexes: IDX_CUSTOMER_EMAIL, IDX_ORDER_DATE
| Aspect | Conceptual | Logical | Physical |
|---|---|---|---|
| Entity | Customer Order | CustomerOrder | CUSTOMER_ORDER (Oracle) / customer_order (PostgreSQL) |
| Attribute | Order Date | order_date | ORDER_DATE / order_date |
| Primary Key | (implied by ER notation) | order_id PK | ORDER_ID, pk_customer_order |
| Foreign Key | relationship line | customer_id FK | fk_custord_customer |
| Relationship | places (verb) | places | (implemented via FK) |
| Constraint | (noted informally) | chk_positive_amount | CHK_CUSTORD_TOTAMT |
Maintain a clear mapping between names at different levels. 'Customer Order' in the conceptual model should obviously correspond to 'CustomerOrder' or 'customer_order' in implementation. Avoid situations where business users call it 'Customer Order' but the table is 'ord_cust'—this disconnection causes endless confusion.
Abbreviations are a major source of naming confusion. Minimize them, and when used, apply them consistently.
The Case Against Abbreviations:
Abbreviations save typing but cost comprehension. Modern SQL editors autocomplete names, making abbreviation savings minimal while comprehension costs are ongoing.
When Abbreviations Are Acceptable:
Universally understood abbreviations:
Domain-standard terms:
When length constraints require:
If your organization must use abbreviations (due to legacy systems or length constraints), maintain an Abbreviation Registry in your data dictionary. Document every approved abbreviation with its full meaning. New abbrevations must be approved and registered before use. This prevents proliferation of inconsistent abbreviations.
Organizations benefit from formal naming standards—documented conventions that all database work must follow.
Components of a Naming Standard:
1. Case Rules:
2. Word Order Rules:
3. Length Limits:
4. Reserved Word Handling:
5. Abbreviation Policy:
6. Prefix/Suffix Rules:
Sample Naming Standard Document:
=== Database Naming Standard v1.0 ===
TABLE NAMES:
- Singular nouns: Customer, Order, Product
- PascalCase: CustomerOrder, ProductCategory
- No prefixes: NOT tbl_Customer
- Maximum 30 characters
COLUMN NAMES:
- snake_case: customer_id, first_name
- Primary key: [table]_id
- Foreign key: [role]_[table]_id if needed
- Boolean: is_*, has_*, can_*
- Dates: *_date for date only, *_at for timestamp
- Maximum 30 characters
CONSTRAINT NAMES:
- Primary key: pk_[table]
- Foreign key: fk_[table]_[referenced_table]
- Unique: uq_[table]_[column(s)]
- Check: chk_[table]_[description]
- Index: idx_[table]_[column(s)]
ABBREVIATIONS:
- Prefer full words
- Allowed: id, qty, amt, sku, url
- Refer to abbreviation registry for others
Enforcement:
A naming standard is only valuable if enforced:
A consistently applied imperfect standard beats an inconsistently applied perfect one. If your organization uses 'CustomerId' everywhere, don't introduce 'customer_id' for new tables. Consistency across the codebase is more valuable than theoretical best practice in isolation.
We've explored naming conventions comprehensively—from first principles of why naming matters to practical standards you can adopt. Good names are not mere aesthetics; they are essential infrastructure for understandable, maintainable databases.
What's next:
With this module on Entities and Entity Sets complete, you now have a thorough understanding of entities—the fundamental building blocks of ER modeling. You understand what entities are, how they're grouped, typed, instantiated, and named. This foundation prepares you for the next modules, which explore attributes, relationships, and more advanced ER concepts.
You've completed Module 2: Entities and Entity Sets. You now understand entities from definition to naming convention—the conceptual, structural, and practical dimensions. These fundamentals will support all your future work in database design. Next modules will build on this foundation with attributes, relationships, and advanced ER concepts.