Table naming guide
Everything worth knowing about naming database tables: the rules the whole industry roughly agrees on, the one big debate nobody agrees on, and the patterns for join tables, history tables and columns.
The rules everyone agrees on
snake_case, lowercase, always
Use lowercase letters with underscores: user_accounts, line_items. PostgreSQL folds unquoted identifiers to lowercase, MySQL is case-sensitive on Linux, and mixed case forces quoting. Lowercase snake_case needs no quoting anywhere.
Descriptive, not clever
order_details beats data and tbl_1. A table name should tell the next developer what rows live inside without opening the schema. Avoid abbreviations nobody agreed on — expires_at, not exp.
No reserved words, ever
order, user, table, group, select and index are reserved in at least one major engine. Naming a table order means quoting it in every query forever. Pick orders or customer_orders instead.
Skip the tbl_ prefix
The table context is always known in a query, so tbl_users adds noise without information. Prefixes only pay off on chaotic shared servers — and even there, a schema name does the job better.
The one big debate
Plural (users) or singular (user)? The plural camp — Rails, Django, most style guides — argues a table holds many rows, so the collection name fits. The singular camp argues SELECT * FROM user reads naturally and matches the model class name. Both are defensible; mixing them is not. We gave the debate its own page: singular vs plural table names.
Join tables and relationships
Many-to-many tables combine both sides in alphabetical order so the name is predictable: categories_products, organization_users. Some frameworks use the singular pivot style instead — role_user. Either way, alphabetical ordering eliminates the "was it users_roles or roles_users?" guessing game. Our generator's join-table mode mints these pairs automatically.
Columns ride along
Table naming sets the tone for the whole schema. The conventions that pair with it:
Column cheat sheet
Columns are singular snake_case: first_name, email. Primary key is id. Foreign keys are singular_table_id — user_id on the orders table. Booleans start with is_ or has_ (is_active). Timestamps end with _at (created_at, updated_at). Never repeat the table name in its own columns — users.name, not users.user_name.
History, log and archive tables
Suffixes earn their keep for table variants: orders_archive, price_history, api_request_log. The base name stays recognizable and the suffix says what the variant is for. This is one of the few places a suffix beats a separate naming scheme.
Ready to mint some? Hit the free table name generator, or browse table name examples for full schemas you can steal.