Your new design is fundamentally OK, but assuming the "get users of given settings" will be a predominant query, you can fine-tune it in the following way...
CREATE TABLE "user" (
user_id INT PRIMARY KEY
-- Other fields ...
);
CREATE TABLE user_setting (
setting INT,
user_id INT,
PRIMARY KEY(setting, user_id),
CHECK (setting BETWEEN 1 AND 125),
FOREIGN KEY (user_id) REFERENCING "user" (user_id)
) ORGANIZATION INDEX COMPRESS;
Note the order of fields in PRIMARY KEY and the ORGANIZATION INDEX COMPRESS clause:
- ORGANIZATION INDEX will cluster (store physically close together) the rows having the same
setting
.
- COMPRESS will minimize the storage (and caching!) cost of repeated
setting
fields.
You can then get users connected to any of the given settings like this...
SELECT * FROM "user"
WHERE user_id IN (
SELECT user_id FROM user_setting
WHERE setting IN (1, 23, 125)
);
...which will be very quick thanks to the favorable indexing and minimized I/O.
You can also get users that have all of the give settings like this:
SELECT * FROM "user"
WHERE user_id IN (
SELECT user_id
FROM user_setting
WHERE setting IN (1, 23, 125)
GROUP BY user_id
HAVING COUNT(setting) = 3
);
Using bitfield for all settings makes it awkward to query and hard to optimize for query performance (in your old design, every query is a table scan!). OTOH, the "column per setting" design would require a separate index per column for good performance and you'd still have some less-than-elegant queries.
Also, these approaches are inflexible, unlike your new design that can be easily extended to accept more settings or to to store additional information about each setting (instead of just number) by adding another table and referencing it from the user_setting
.