【问题标题】:PostgreSQL - How to identify three roles of users in database?PostgreSQL - 如何识别数据库中用户的三个角色?
【发布时间】:2016-02-06 01:31:09
【问题描述】:

我正在构建一个需要三种用户角色的数据库架构:普通用户、管理员和超级管理员。我的第一种方法是创建两个表,一个用于普通用户,一个用于从 users 表继承的管理员和超级管理员。所以表定义是这样的:

CREATE TABLE users (
  id BIGSERIAL NOT NULL PRIMARY KEY,
  email TEXT NOT NULL UNIQUE,
  password TEXT NOT NULL,
  name TEXT NOT NULL,
  gender CHAR NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE admins (
  phone BIGINT NOT NULL,
  doc BIGINT NOT NULL,
  super_admin BOOLEAN NOT NULL,
  id BIGINT NOT NULL PRIMARY KEY REFERENCES users(id)
);

但我不确定如何在需要时选择用户或管理员。所以现在我在想是否最好只留下一个“用户”表并定义一个新表“角色”,用户将在其中匹配,以便使用指定用户是否正常的列“角色”来选择它们、管理员或超级管理员。

【问题讨论】:

  • 您可以有一个新的角色表,并在您的用户表中有一个 role 列和一个 FK 引用。问题是,如果有一天你想让用户拥有多个角色,这会稍微困难一些,因为这意味着多对多关系,这通常意味着一个中间表。您可能想尝试使用 Flag 枚举方法,其中分配 Super Admin = 1、Admin =2、Normal = 4、Supervisor = 8 等。现在用户可以拥有Normal | Supervisor 的角色,并且用于检查主管的 where 子句变为(user.role & 8) = 8.
  • @Sourav'Abhi'Mitra 因此,如果用户正常开始,但他想同时成为普通用户和管理员,它只会在“标志”列中更改?例如,他开始为 4,然后变为 5(正常 + 管理员含义)。
  • @licuadolala 是的,虽然 (normal+admin) 将是 (4+2) = 6,但这确实意味着一些额外的处理逻辑。

标签: sql database postgresql database-design


【解决方案1】:

首先,您不应该将电话号码存储为整数。你应该使用字符串。世界上有有效的电话号码可以以0开头,整数无法处理。

这可能很复杂。首先,我注意到您有两个额外的数据字段用于管理员和超级用户。如果您将数据包含在users 表中,那么任何有权访问users 的人都将拥有此信息。通常,这很好。但是,我可以想象这是一个问题的情况。

Postgres 直接支持表继承(参见here),因此您不需要显式的外键关系。继承是处理派生用户类型的好方法。它为您提供了 adminssupers 的替代表,您可以在 from 子句中引用它们(这些也可供用户使用)。

有两种选择。一种是将额外的列(phonedoc)与类型指示符一起放入users 表中。这可以是单个字段(“normal”、“admin”、“super”)或三个标志(“IsNormal”、“IsAdmin”和“IsSuper”)。另一种方法是有一个单独的UserRoles 联结表。

这些方法中的每一种都有其优点。因为您已经开始使用多个表,并且您已经为管理员和超级用户提供了增强的数据,所以对于这种特定情况,我可能会建议继承。请注意,语法可能是:

CREATE TABLE admins (
  phone BIGINT NOT NULL,
  doc BIGINT NOT NULL
) inherits (users);

CREATE TABLE supers (
  phone BIGINT NOT NULL,
  doc BIGINT NOT NULL
) inherits (users);

【讨论】:

  • 在第一种选择中,如果所有列都是强制性的怎么办?例如,所有角色都使用单个字段定义,如果我想添加普通用户,我应该将 phone 和 doc 列保留为空字符串?
  • @licuadolala 。 . .在我看来,您应该将它们设为 NULL 而不是空字符串。
  • 谢谢,我有最后一个问题。是为每个角色创建一个新用户还是更新现有用户中的列更好?
  • @licuadolala 。 . .可能是为了更新现有用户,但在您尝试执行的操作中可能还有其他因素可能会建议创建一个新用户。
【解决方案2】:

这可以通过引入两个新表轻松解决:

  1. 角色 - 这将包含所有类型的角色
  2. user_roles- 用于将用户映射到角色。

样本数据:

 Roles
 -------------------
 |ID |role_type   | 
 -------------------
 |1 |Admin        |
 |2 |Normal       |
 |3 |Super Admin  |
 -------------------

 user_roles
 ------------------
 |user_id |role_id|
 ------------------
 |1      |1       |
 |1      |2       |
 |2      |3       |
 ------------------

user_roles 表将有 FK referenceusersroles 表。

【讨论】:

    【解决方案3】:

    您可以在标量子查询中使用 exists(),它会产生一个布尔表达式:

    INSERT INTO users(email,name,password,gender) VALUES
            ( 'john@world', 'John' , 'John', 'S' ),
            ( 'mary@world', 'mary' , 'mary', 'S' ),
            ( 'Jeff@world', 'Jeff' , 'Jeff', 'J' ) ;
    
    INSERT INTO admins(phone, doc, super_admin, id) VALUES
            ( '007' , 0, False, 2),
            ( '008' , 0, True, 3) ;
    
    SELECT email,name,gender
            , (EXISTS (SELECT 1 FROM admins a WHERE a.id = u.id)) AS is_admin
            , (EXISTS (SELECT 1 FROM admins a WHERE a.id = u.id AND a.super_admin)) AS is_super_admin
    FROM users u
            ;             
    

    结果:

    CREATE TABLE
    INSERT 0 3
    CREATE TABLE
    INSERT 0 2
       email    | name | gender | is_admin | is_super_admin 
    ------------+------+--------+----------+----------------
     john@world | John | S      | f        | f
     mary@world | mary | S      | t        | f
     Jeff@world | Jeff | J      | t        | t
    

    请注意:原始数据模型可能并不完美,但查询至少解决了手头的问题。 (你可以隐藏它——例如*在一两个视图中)

    【讨论】:

      猜你喜欢
      • 2014-03-30
      • 1970-01-01
      • 2019-02-16
      • 2020-05-16
      • 1970-01-01
      • 2011-03-13
      • 1970-01-01
      • 2015-09-04
      • 1970-01-01
      相关资源
      最近更新 更多