【问题标题】:Arrays in database tables and normalization数据库表中的数组和规范化
【发布时间】:2023-03-17 10:21:01
【问题描述】:

将数组保存在表格列中是否明智?更准确地说,我正在考虑以下模式,据我所知,它违反了规范化:


create table Permissions(
    GroupID int not null default(-1),
    CategoryID int not null default(-1),
    Permissions varchar(max) not null default(''),
    constraint PK_GroupCategory primary key clustered(GroupID,CategoryID)
);

还有这个:


create table Permissions(
    GroupID int not null default(-1),
    CategoryID int not null default(-1),
    PermissionID int not null default(-1),
    constraint PK_GroupCategory primary key clustered(GroupID,CategoryID)
);

UPD3:我将权限设想为逗号分隔的字符串,因为 MSSQL 是我们的主要部署目标。

UPD:忘了提,在这个具体问题的范围内,我们将考虑不会执行“获取具有权限 X 的行”,而是所有查找将仅由 GroupID 和 CategoryID 进行

UPD2:我设想的典型使用场景如下:


int category_id=42;
int[] array_of_groups=new int[]{40,2,42};
if(!Permissions.Check(category_id, array_of_groups, Permission.EatAndDrink)) {
    throw new StarveToDeathException();
}

想法?

提前致谢!

【问题讨论】:

    标签: sql arrays normalization


    【解决方案1】:

    第一个实现的问题在于它实际上并没有使用数组,而是使用连接的字符串。

    这意味着您将无法轻松使用存储在该字符串中的值来执行基于集合的查询,例如查找具有特定权限或特定权限集的所有人。

    如果您使用的是原生支持数组作为原子值的数据库,例如 PostgreSQL,那么参数会有所不同。

    根据提议的查询的第二个要求,我不得不建议第二个最好,因为您可以简单地查询SELECT count(*) FROM Permissions WHERE CategoryID = 42 AND GroupID IN (40, 2, 42) AND PermissionID = 2(假设 EatAndDrink 的 ID 为 2)。然而,第一个版本需要检索每个组的所有权限并解析字符串,然后才能测试它是否包含请求的权限。

    【讨论】:

    • 忘了提一下,在这个具体问题的范围内,我们将考虑不会执行“获取具有权限 X 的行”,而是所有查找将仅由 GroupID 和 CategoryID 进行
    • 现在,直到您被要求生成在某个时间点在系统中拥有权限 X 的所有用户的报告。
    • 我几乎相信:) 但是“GroupID IN (40, 2, 42)”需要 MSSQL 中的动态 SQL 或他们在 sommarskog.se/arrays-in-sql-2005.html 建议的任何其他内容。
    【解决方案2】:

    你的第二个例子应该是:

    constraint PK_GroupCategory primary key clustered(GroupID,CategoryID,PermissionID)
    

    您的第一个示例将违反正常形式(并且字符串解析可能不能很好地利用您的处理时间),但这并不意味着它对您的应用程序来说一定是错误的。这真的取决于您如何使用数据。

    【讨论】:

    • @Ivan Petrov 我会说这需要使用 PermissionID 进行规范化(看起来只是存在表示 GRANT,但通常您可能对对象拥有更多权限),就像在您的表模式中一样。在内心深处,你知道这是对的。
    【解决方案3】:

    聪明吗

    有时,这取决于。我会说这取决于你对标准化事物的定义有多狭窄。

    如果您看不到每个项目只有一行的表格永远不会有用,那么我建议可以考虑封装在字符串中。

    在给出的示例中,如果我必须编写使用字符串模式匹配的 WHERE 子句,我想确保执行查询以查找指定权限的所有组/类别组合不会给我带来问题。当然,如果我永远不必执行这样的查询,那就没有实际意义了。

    一般来说,我对这种方法最满意,因为要组装的数据因此孤立地没有意义:数据只有在被视为一个完整的集合时才有意义。如果有更多的结构,比如数据/值对列表,那么使用 XML 或 JSON 格式化会很有用。

    【讨论】:

    • 我将 Permissions 设想为逗号分隔的字符串,因为 MSSQL(我们的主要部署目标)不支持数组,实际上我个人就是否需要支持数组进行了很大的争论,因此这个问题 :)跨度>
    【解决方案4】:

    如果您仅通过 GroupID 和/或 CategoryID 进行查询,则没有任何问题。规范化意味着更多的表、行和连接。因此,对于大型数据库,这可能会对性能产生负面影响。

    如果您绝对确定您永远不需要处理权限的查询,并且它仅由您的应用程序解析,那么此解决方案没有任何不妥之处。如果您总是想要完整的权限集(即您不只是为了获取字符串的一部分,而是始终想要它的所有值),它也可能更可取。

    【讨论】:

    • 好点,鉴于我希望它们灵活,我不能确保触发器的完整性吗?不过这对我来说似乎太麻烦了……
    • 好吧,通过使用字符串,您基本上选择让应用程序完全处理数据。如果您担心它的完整性,我会规范化数据并使用第二种解决方案(PermissionIDs 而不是字符串)。
    【解决方案5】:

    我建议走规范化道路,原因如下:

    • 通过拥有一个包含所有可能权限的表格,您就拥有了自记录数据。您可以为每个权限添加说明。这绝对胜过没有任何意义的串联 id 值。
    • 您可以获得参照完整性的所有优势,并且可以确保您的数据中没有虚假的权限 ID。
    • 插入和删除权限会更容易 - 您可以添加或删除记录。使用连接的字符串,您将更新一列,并仅在您删除最后一个权限时删除记录。
    • 您的设计是面向未来的 - 您说您只想通过 CategoryID 和 GroupID 进行查询,您已经可以使用规范化表来执行此操作。最重要的是,您还可以将其他属性添加到您的权限、按权限查询等。
    • 性能-明智地,我认为获得 id 的结果集实际上比将字符串解析为整数要快。以实际数据和实施来衡量...

    【讨论】:

      猜你喜欢
      • 2016-06-06
      • 2015-02-15
      • 1970-01-01
      • 1970-01-01
      • 2015-06-10
      • 1970-01-01
      • 2021-06-18
      • 2021-10-27
      相关资源
      最近更新 更多