Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

For most database schemas, name the primary key id in the users table and name columns that reference it user_id. Use user_id as a primary key when the row is a one-to-one extension of a user, such as a user’s settings. The name is a convention; the more consequential decisions are what uniquely identifies the row, which constraints enforce that, and whether the key should be an integer, UUID, or another value.

What the names mean

In users.id, id identifies a row in the users table. In posts.user_id, user_id identifies the user associated with that post. A common convention is therefore:

  • Use id for an entity table’s own primary key.
  • Use <entity>_id for a foreign key to that entity.

For example:

CREATE TABLE users (
    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    email TEXT NOT NULL UNIQUE
);

CREATE TABLE posts (
    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    user_id BIGINT NOT NULL REFERENCES users(id),
    title TEXT NOT NULL
);

SELECT users.id, posts.id
FROM posts
JOIN users ON users.id = posts.user_id;

The naming makes each column’s role apparent. Prefixing every primary key with its table name—users.user_id, posts.post_id—is also valid if it is your team’s consistent standard. Conversely, calling every key simply id can require table qualification or aliases in joins. Neither naming style changes the underlying key behavior.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When user_id should be the primary key

If a table can contain at most one row per user and that row has no independent identity, the user’s key can identify it directly:

CREATE TABLE user_settings (
    user_id BIGINT PRIMARY KEY REFERENCES users(id),
    timezone TEXT NOT NULL,
    marketing_opt_in BOOLEAN NOT NULL
);

Here, user_id does two jobs: it references a real user and, through the primary-key constraint, prevents a second settings row for the same user. The same pattern can suit a profile or preferences table.

If other records need to refer to the settings or profile as an independent entity, give it its own id and make user_id unique instead:

CREATE TABLE user_profiles (
    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    user_id BIGINT NOT NULL UNIQUE REFERENCES users(id),
    display_name TEXT
);

The UNIQUE constraint is what makes the relationship one-to-one. A foreign key alone only requires the referenced user to exist; it does not prevent multiple child rows from pointing to that user.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Does every table need an id column?

No. A table needs a dependable way to distinguish its rows, but that key need not be a single generated id. A primary key can contain multiple columns, and a stable, authoritative natural key can be appropriate. PostgreSQL describes primary keys as unique and non-null and supports composite keys; see its constraint documentation.

Many-to-many relationships

A join table is often identified by the pair of records it connects:

CREATE TABLE user_roles (
    user_id BIGINT NOT NULL REFERENCES users(id),
    role_id BIGINT NOT NULL REFERENCES roles(id),
    PRIMARY KEY (user_id, role_id)
);

This rules out assigning the same role to the same user twice. Add a separate id only if the relationship row itself needs an independent identifier. If you do, keep UNIQUE (user_id, role_id); a surrogate key does not enforce that business rule by itself.

Natural keys

A compact, stable, mandatory, authoritative value—such as a country’s ISO code—can be a suitable primary key. Many values that look like natural keys are less reliable: email addresses, usernames and phone numbers can change or have normalization and collation issues. A common approach is a surrogate key plus a separate uniqueness rule, such as email TEXT NOT NULL UNIQUE. A generated key does not make email unique unless the database constraint says so.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose the key type separately from its name

id versus user_id is a naming choice. Integer versus UUID is an identity-design choice.

  • Integer or bigint: Often a practical internal key: compact, straightforward to inspect, and efficient to carry in foreign keys. A single database or coordinated allocator can generate values. Sequential values may reveal approximate insertion order and are guessable if exposed, and allocation across independent writers requires planning.
  • UUID: Useful when records must be generated by independent systems or before database insertion, or when cross-database uniqueness matters. UUIDs take more space than integer keys and can be less readable; index and insertion behavior depends on the UUID version, storage representation and database. PostgreSQL has a native uuid type and documents UUIDs as 128-bit identifiers: PostgreSQL UUID documentation.

Neither kind of key provides authorization. A sequential integer may be easy to enumerate, while a UUID may be harder to guess, but an application must still check whether the requester is allowed to access the requested record.

Some systems keep a compact internal key and a distinct public identifier:

CREATE TABLE users (
    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    public_id UUID NOT NULL UNIQUE
);

Use this only when the separate external identifier solves a real integration, URL or exposure requirement. It adds another value to manage and a uniqueness index. Do not add both id and user_id to users without clearly distinct roles.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Constraints and indexes still matter

The column name does not enforce a relationship. Use a primary-key constraint for row identity, a foreign-key constraint for referential integrity, and UNIQUE for business rules such as one profile per user or one membership per team-user pair.

Rank #4
Sale
SQL Database Query Programmer T-Shirt
  • Database Programming design. Funny database SQL joke that makes a great gift for database administrators, programmers or computer scientists. Fun gift for database administrators, programmers and hackers who like to wear funny nerd clothes.
  • Funny gift for men and women who love SQL. The perfect SQL Query top for programmers, hackers and SQL database fans who love relational databases.
  • Lightweight, Classic fit, Double-needle sleeve and bottom hem

Consider indexing foreign-key columns used in joins, filtering, or deletes. PostgreSQL creates an index for a primary key, but does not automatically index the referencing columns of a foreign key; consult its constraint guidance and assess the workload. Indexing behavior and costs vary by database.

Key width can matter even though the key’s name does not. In InnoDB, primary-key values are included in secondary-index entries, so a wider key can increase index storage. MySQL documents this behavior and its primary-key rules in CREATE TABLE.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Database syntax varies

The naming recommendation is broadly portable, but value-generation syntax and key behavior differ:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Quick Recap

SaleBestseller No. 1
SaleBestseller No. 4
SQL Database Query Programmer T-Shirt
SQL Database Query Programmer T-Shirt
Lightweight, Classic fit, Double-needle sleeve and bottom hem
$16.99
  • PostgreSQL: For a generated integer key, use an identity column, for example BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY. Choose ALWAYS or BY DEFAULT based on whether explicit values must be accepted for imports or migrations. See identity and default values.
  • MySQL with InnoDB: A typical form is BIGINT UNSIGNED NOT NULL AUTO_INCREMENT with a primary-key constraint. InnoDB’s secondary-index storage makes key width worth considering. See the MySQL table-creation reference.
  • SQLite: INTEGER PRIMARY KEY has special rowid behavior. Do not add AUTOINCREMENT reflexively; it has distinct semantics. See SQLite CREATE TABLE.
  • SQL Server: IDENTITY generates values; it does not itself declare a primary key. Define the key constraint separately. See SQL Server IDENTITY property.

Practical rules to avoid mistakes

  • Use role-specific foreign-key names when a table references the same entity more than once: sender_id and recipient_id are clearer than user1_id and user2_id.
  • Do not add UNIQUE(user_id) to a posts table if a user can have many posts. Add it only for a genuine one-to-one relationship.
  • Do not assume auto-generated IDs have no gaps or reliably encode creation time. Store created_at when you need a timestamp.
  • Do not treat a UUID or an obscure column name as an access-control mechanism.
  • Choose one table-naming and key-naming convention for the project and apply it consistently.

Quick decision guide

  1. Ordinary entity such as users, posts or orders? Use id as its primary key and name references user_id, post_id or order_id.
  2. One dependent row per user, with no independent identity? Make user_id the child table’s primary key and a foreign key to users.id.
  3. Relationship identified by a pair? Consider a composite primary key such as (team_id, user_id).
  4. Stable authoritative natural identifier? It may be the primary key, but preserve all required uniqueness and validation rules.
  5. Need keys from independent writers or a public opaque identifier? Consider a UUID, or a separate public UUID alongside an internal key; neither replaces authorization.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.