PHP&SQL文档共享系统数据库设计优化咨询
嘿,我来帮你捋捋这个权限表设计的问题——你担心的冗余问题其实是关系型数据库里很常见的误区,咱们一步步说清楚:
原权限表设计的合理性(为啥它其实不冗余)
你现在用的permissions表是多对多关联表(junction table),这是关系型数据库处理「一个文档对应多个用户、一个用户对应多个文档」这种关系的标准做法。看起来200个用户就有200条记录,但这其实不是冗余——关系型数据库就是靠这种结构化的“重复”来保证数据的规范性和操作效率:
- 索引效率拉满:给
(document_id, user_id)加个联合主键/索引,不管是查询用户可访问文档、给用户授权、取消授权,数据库都能瞬间定位到目标记录,哪怕有十万条记录也毫无压力 - 数据一致性有保障:数据库的约束能直接避免同一个用户被重复授权同一个文档,不用靠业务代码去额外校验
- 维护成本极低:新增/删除单个用户权限,只需要增删一条记录就行,逻辑简单不容易出错
200条记录对现代数据库来说真的是小case,完全不用纠结这个量级的“冗余”。
改用
users_ids字段的坑(不推荐的原因) 如果改成用单个字段存多个用户ID(比如逗号分隔的字符串),会踩一堆坑:
- 查询性能暴跌:要判断用户是否有权限,得用
FIND_IN_SET(user_id, users_ids)这种函数,这种查询完全用不上索引,等你的文档和用户量上来后,查询速度会慢到难以接受 - 维护逻辑超级麻烦:给文档加用户权限,得先读出现有ID字符串、拆分、检查是否重复、再拼接更新;删权限还要拆分字符串、过滤掉目标ID、再拼接回去,不仅代码啰嗦,并发操作时还容易出现数据冲突
- 扩展性为0:以后如果要加权限等级(比如只读、可编辑、可评论),这种结构根本没法支持,只能推翻重来
- 数据隐患多:很容易出现格式错误(比如漏写逗号、ID重复),而且数据库没法帮你校验,只能靠业务代码兜底
非要用
users_ids的实现方案(仅作参考) 如果你因为某些特殊场景一定要用这种结构,那可以这么实现:
数据库表调整
CREATE TABLE permissions ( document_id INT PRIMARY KEY, -- 每个文档对应一条记录 user_ids TEXT NOT NULL -- 存储逗号分隔的用户ID,例如 '12,34,56' );
PHP&SQL操作示例
1. 给文档添加用户权限
// 示例:给document_id=10的文档添加user_id=78的权限 $documentId = 10; $userId = 78; // 先查询该文档已有的权限记录 $stmt = $pdo->prepare("SELECT user_ids FROM permissions WHERE document_id = ?"); $stmt->execute([$documentId]); $row = $stmt->fetch(PDO::FETCH_ASSOC); if ($row) { // 已存在记录,先检查用户是否已在权限列表中 $existingIds = explode(',', $row['user_ids']); if (!in_array((string)$userId, $existingIds)) { $newIdsStr = implode(',', array_merge($existingIds, [(string)$userId])); $updateStmt = $pdo->prepare("UPDATE permissions SET user_ids = ? WHERE document_id = ?"); $updateStmt->execute([$newIdsStr, $documentId]); } } else { // 不存在记录,直接插入新行 $insertStmt = $pdo->prepare("INSERT INTO permissions (document_id, user_ids) VALUES (?, ?)"); $insertStmt->execute([$documentId, (string)$userId]); }
2. 查询用户可访问的文档
$userId = 12; // 使用FIND_IN_SET判断用户ID是否在权限列表中 $stmt = $pdo->prepare(" SELECT d.document_title FROM documents d JOIN permissions p ON d.document_id = p.document_id WHERE FIND_IN_SET(?, p.user_ids) "); $stmt->execute([$userId]); $accessibleDocs = $stmt->fetchAll(PDO::FETCH_ASSOC);
3. 删除用户的文档权限
$documentId = 10; $userId = 78; $stmt = $pdo->prepare("SELECT user_ids FROM permissions WHERE document_id = ?"); $stmt->execute([$documentId]); $row = $stmt->fetch(PDO::FETCH_ASSOC); if ($row) { $existingIds = explode(',', $row['user_ids']); // 过滤掉要删除的用户ID $newIds = array_filter($existingIds, function($id) use ($userId) { return $id != (string)$userId; }); if (empty($newIds)) { // 权限列表空了,直接删除整条记录 $deleteStmt = $pdo->prepare("DELETE FROM permissions WHERE document_id = ?"); $deleteStmt->execute([$documentId]); } else { $newIdsStr = implode(',', $newIds); $updateStmt = $pdo->prepare("UPDATE permissions SET user_ids = ? WHERE document_id = ?"); $updateStmt->execute([$newIdsStr, $documentId]); } }
最终建议
强烈建议你保留原有的关联表设计,只需要给permissions表加个联合主键就行:
ALTER TABLE permissions ADD PRIMARY KEY (document_id, user_id);
这一步既能防止重复授权,又能让所有权限相关的查询、操作效率达到最优。如果以后要扩展权限功能(比如区分只读/编辑权限),只需要给permissions表加个permission_type字段(比如'read'、'edit'),扩展性拉满。
内容的提问来源于stack exchange,提问作者Jamal Salim
相关产品推荐
相关产品推荐

