SELECT ...
FROM users u
LEFT JOIN userroles ur ON ur.user_id = u.user_id AND ur.role_id = 2
WHERE ur.role_id IS NULL
ORDER BY u.user_id ASC;
Examine all rows of user, and their matching row from userroles with a role_is = 2 if it exists, eliminating the rows where it does indeed exist by requiring the role_id in the (non-existent) row to be null... which, since the row does not exist, is indeed null.
Alternately,
SELECT ...
FROM users u
WHERE NOT EXISTS (SELECT * FROM userroles ur WHERE ur.user_id = u.user_id AND ur.role_id = 2)
ORDER BY u.user_id ASC;
Note that the SELECT *
in the subquery doesn't actually select everything, it's just a way to express the test of whether such a row exists.
I would begin with a little tweak:
In your current design, there is no need to have "id" column on vocabularylists table. I will write it from top of my head, so apologies in advance for any syntactic mistakes. :)
First change:
CREATE TABLE IF NOT EXISTS `vocabularylists` (
`name` varchar(20) UNIQUE NOT NULL,
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
);
This way, you will have numeric ID and easily modifiable name of the vocabulary. This will make occasional renaming of vocabulary list a bit easier. But, of course, you can use only name
columnt and totally strip out the id
.
Second change:
You need to connect the vocabularies to vocabularylists with a foreign key constraint. The vocabularies table will look like this:
CREATE TABLE IF NOT EXISTS `vocabularies` (
`vocabulary` varchar(20) NOT NULL,
`translation` varchar(20) NOT NULL,
`list_id` int(11),
FOREIGN KEY (list_id) REFERENCES vocabularylists(id) ON DELETE CASCADE
);
By creating foreign key, you ensure so-called referential integrity: from now on, your database cannot store vocabulaies that do not belong to a vocabulary list. Also (as ensured by ON DELETE CASCADE
) any deletes on vocabularylists automaticcaly result to deletion of corresponding entries on vocabularies.
Third change:
Querying the vocabularies table will be slow. Put indexes on your tables:
CREATE TABLE IF NOT EXISTS `vocabularylists` (
`name` varchar(20) UNIQUE NOT NULL,
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
INDEX `name_idx` (`name`)
);
CREATE TABLE IF NOT EXISTS `vocabularies` (
`vocabulary` varchar(20) NOT NULL,
`translation` varchar(20) NOT NULL,
`list_id` int(11),
FOREIGN KEY (`list_id`) REFERENCES vocabularylists(`id`) ON DELETE CASCADE,
INDEX `vocabulary_idx` (`vocabulary`),
INDEX `translation_idx` (`translation`)
);
This will give you much better performance.
A hint:
You have a very simple structure defined here. With little data, there will be no significant problems with performance. But the only thing you really need - looking at those two db tables - is a key-value store. So instead of a database, you could easily use a plain/CSV/... file which will be loaded on application startup into some in-memory store (at the beginning a hashmap, then something like Redis maybe).
Also a word in one language can have multiple meanings in another. Your database - as it is - cannot store those things. I don't know if it is desired behavior but it will be limiting you in case you are extending your application.
Best Answer
The correctness of a database design is specific to the requirements that you gather prior to building it. In simple terms, one size does not fit all and in many situations you will find more than one solution to your problem. The skill is in determining which solution best fits the requirements that you have. There will be occasions when the solution you choose is wrong and you must go back and fix it, it happens to everyone, don't get disheartened about it.
Regarding your specific case, I wouldn't say that there is anything that is bad with what you have chosen, instead I would give you some guidance.
You have a "base" table called
users
from whichcustomers
,managers
andemployees
extend. This is fine, just ensure that any columns that are common between all derived types are in theusers
table. Only data specific to the derived types should be in those tables.My personal preference for role management is to have a
roles
table and then have auser_roles
table which implements a many-to-many relationship as it may be possible for a user to be in more than one role. However, if it is not possible for a user to be in more than one role then you could add an additional key to theusers
table which references the role that they are in.I still prefer the additional table design myself because its easier to scale. So for instance if you decided that initially a user can only belong to one role, then you would just place a unique constraint over the role and user id columns. In the future, if it becomes possible or required that a user can be in multiple roles it is far easier to drop that constraint than it is to redesign your database.
Keep it simple, and try not to repeat yourself. Remember that database design is not just about the now, it is about attempting to plan for the future as well - you don't want to have to keep redesigning it every week (not to say that doesn't happen sometimes).