选项 2 比选项 1 好。但是,相对而言,它仍然不好。
永远不要在数据列中存储以逗号分隔的事物列表。如果你这样做了,你会后悔的。 (它们的搜索成本非常高。)
你想要这样的东西。三张表,一张给用户,一张给服务,还有一张所谓的JOIN表来建立两者之间的多对多关系。
+-----------+ +-------------+ +-----------+
|user | |user_service | |service |
+-----------+ +-------------+ +-----------+
|user_id +--->|user_id |<----+service_id |
|givennamee | |service_id | |name |
|surname | +-------------+ +-----------+
|is_active |
+-----------+
user_service 中的每一行表示用户有权使用该服务。要授权用户,请插入一行。要撤销授权,请删除该行。
要了解用户是否可以使用服务,请使用此查询。
SELECT user.user_id
FROM user
JOIN user_service USING (user_id)
JOIN service USING (service_id)
WHERE user.givenname = 'Bill' AND user.surname='Gates'
AND service.name = 'CharityNavigator'
AND user.is_active > 0;
如果您的查询返回user_id,则所选用户可以使用所选服务。
要获取每个用户的服务列表,请使用此查询。
SELECT user.user_id, user.givenname, user.surname,
GROUP_CONCAT(service.name) service_names
FROM user
JOIN user_service USING (user_id)
JOIN service USING (service_id)
WHERE user.is_active > 0
GROUP BY user.user_id
一些解释:
几乎总是最好为其中的服务等内容构建包含行的表,而不是列中的列或逗号分隔的列表。为什么?
-
您可以在多年后添加新服务 - 任意数量 - 无需重新编写数据库代码。
-
包括 MySQL 在内的 DBMS 可以很好地与 JOIN 操作配合使用。
-
在大多数关系数据库管理系统中,执行WHERE commalist_column SOMEHOW_CONTAINS (some_id) 的效率低得令人作呕。执行WHERE column = some_id 效率更高,因为它可以使用索引。
-
通常,列数较少的行比列数多的行效果更好。
-
在生产环境中向数据库添加行比添加列要便宜得多。添加列意味着更改表定义。该操作可能需要停机。
当您将列用于服务等内容时,您正在创建一个封闭的系统。当您使用行时,您的系统是开放式的。
我可以建议您阅读有关database normalization 的信息吗?不要被所有 CS 行话吓倒。只需看一些如何规范化各种数据库的示例。
也许阅读entity-relationship database modeling?
编辑根据评论者的建议,我建议您将user_service 表的主键设置为包含(user_id, service_id) 两列。我还建议您使用 (service_id, user_id) 两列创建一个反向索引,以便您的查询可以从服务和用户开始快速查找。您的表定义可能如下所示:
CREATE TABLE user (
user_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
givenname VARCHAR(50) NULL DEFAULT NULL,
surname VARCHAR(50) NULL DEFAULT NULL,
is_active TINYINT NOT NULL DEFAULT '1',
PRIMARY KEY (user_id)
)
COLLATE='utf8mb4_general_ci';
CREATE TABLE service (
service_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (service_id)
)
COLLATE='utf8mb4_general_ci';
CREATE TABLE user_service (
user_id INT UNSIGNED NOT NULL,
service_id INT UNSIGNED NOT NULL,
PRIMARY KEY (user_id, service_id),
INDEX reverse_index (service_id, user_id),
CONSTRAINT FK_service
FOREIGN KEY (service_id)
REFERENCES service (service_id)
ON UPDATE RESTRICT ON DELETE RESTRICT,
CONSTRAINT FK_user
FOREIGN KEY (user_id)
REFERENCES user (user_id)
ON UPDATE RESTRICT ON DELETE RESTRICT
);
如果您尝试使用此主键为用户插入重复的服务授权,则 dbms 会拒绝它。
确保在这些表中使用相同的“INT UNSIGNED NOT NULLdata type foruser_idandservice_id”。
这是一种非常常见的数据库设计模式:它是在两个不同表的行之间创建多对多关系的规范方式。