Reputation: 829
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_users
and 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
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