DazedAndConfused
DazedAndConfused

Reputation: 829

Best way to mimic inheritance in postgresql?

For an application I am writing, there are two types of "users", those who have made accounts and those who have not, virtual_users. These two types are nearly identical, except account_users have a password, and email is required and must be unique amongst all account_users, although it can be the same as any number for virtual_users. A large number of tables have a column that references users, which should include both, and 90% of app functionality treats them as interchangeable. What is the best way of handling this? Some options I have considered:

-Put both types of users in the same table and have a complicated constraints regarding uniqueness, basically, if password is not NULL, email must be unique among all users where password is not NULL. I have no idea how I would write this constraint. On the few occasions I only want account_users query for only users who have a password. This seems like the best solution if I can figure out how to write the constraint.

-Have Account_users inherit from Virtual_usersand Virtual_users has an additional column password and unique constraints on email. From here there are two potential options:

---Have a Users table which includes two columns account_user_id and virtual_user_id one of which is NULL and one of which corresponds to the appropriate user. When other tables need to reference a user, they reference this table. Have all my queries server side for users query both tables and combine.

---When other tables need to reference they reference either table. I don't think this is possible. Have all my queries server side for users query both tables and combine.

Any advice would be appreciated.

Upvotes: 0

Views: 80

Answers (1)

Schwern
Schwern

Reputation: 165396

I assume the scenario is you have a system which some parts require the user to be signed into a registered account, and others do not, but you'd still like to track users.

Postgres has table inheritance. You could use that, but I'd be concerned about the caveats.

You could put them all into one table and use some sort of flag, like Single Table Inheritance, but then you run into constraint issues. You would then enforce constraints in the model. This should be fine if you have a strong model.

You could have separate accounts and users. Rather than one being a special case of the other, they key is thinking of them as two conceptually different things. In OO terms, an account has a user.

-- Visitors to the site who haven't signed up.
create table users (
    id          serial,

    -- The unverified email they might have given you.
    email       text,

    -- Any other common information like a tracking token
    token       text
);

-- Users who have registered.
create table accounts (
    id          serial,
    user_id     int references users(id),

    -- Their verified email.
    email       text not null,
    -- Hashed, of course.
    password    text not null

    -- any additional information only for accounts
);

I like this because there are no flags involved, users and accounts can have separate constraints, and the accounts table doesn't get bloated with users that showed up once and never came back.

You'd access complete account information with a simple join.

select accounts.*, users.token
from accounts
join users on accounts.user_id = users.id
where accounts.id = ?

If you want to delete an account you can do so without losing the underlying user information.

delete from accounts where accounts.id = ?

Systems which require an account use accounts. Systems which don't use users. Systems which behave differently for users and accounts can check if a user has an account easily.

select accounts.id
from accounts
where accounts.user_id = ?

User tracking is associated with the users table, so you have a consistent record of a user's behavior before and after they register.

Upvotes: 2

Related Questions