【问题标题】:Join tables with enum values使用枚举值连接表
【发布时间】:2021-07-22 15:32:45
【问题描述】:

这可能是@saeedehp 回答的后续问题Enum Join。我有一个充满枚举值的表,它们连接在一起以在另一个表中构建一些用户角色列。

例子

表 1 枚举表

Role       - RoleEnumValue
-------------------------
Student    - 1
Researcher - 2
Lecturer   - 4
Teacher    - 8

表 2 用户表

UserName   - Roles
-------------------------

John       - 3 
Melvin     - 14 

结果

UserName  - UserRoles
--------------------------------
John      - Student, Researcher
Melvin    - Researcher, Lecturer, Teacher

这是获取结果的 sql 查询。但是我需要为表 1 中的每一行编写 switch case

SELECT  t.UserName
    , ISNULL(t.C1 + ', ', '') + ISNULL(t.C2, '') + ISNULL(', ' + t.C3, '') + 
      ISNULL(', ' + t.C3, '') AS [UserRoles]
FROM
(
    SELECT UserName,
        CASE WHEN (Roles & 1) <> 0  THEN 'Student' END AS C1,
         CASE WHEN (Roles & 2) <> 0  THEN 'Researcher' END AS C2,
         CASE WHEN (Roles & 4) <> 0  THEN 'Lecturer' END AS C3,
         CASE WHEN (Roles & 8) <> 0  THEN 'Teacher' END AS C3
         ,... -- if i have more rows
    FROM "Table 2"
) t

现在的问题是我的表 1 有大约 14 到 20 个角色,继续添加 switch case 没有效率。

提取多个角色并获得上述结果的另一种方法是什么? 能否指出这可能属于哪个维恩图?

(我使用的是 Microsoft SQL Server)


更新

我想我要解决的问题是快速找到用户角色,而不需要打开应用逐个检查用户。

@Gordon Linoff 的精彩回答,

@MatBailie 的另一个很棒的答案,它让我意识到答案很简单。

解决办法。

SELECT 
u.UserName, 
STRING_AGG(r.Role, ',') AS AssignedRoles
FROM "Table 2" u
LEFT JOIN "Table 1" r ON r.RoleEnumValue & u.Roles <> 0
GROUP BY u.UserName

【问题讨论】:

  • 很明显这是一个常见问题解答。在考虑发布之前,请阅读手册和谷歌任何错误消息以及您的问题/问题/目标的许多清晰、简洁和精确的措辞,带有和不带有您的特定名称/字符串/数字、“site:stackoverflow.com”和标签;阅读许多答案。如果您发布问题,请使用一个短语作为标题。反映你的研究。请参阅How to Ask 和投票箭头鼠标悬停文本。 PS 当您发布代码问题时,它应该包含minimal reproducible example
  • 能否重新设计数据库结构?将关系编码为位并不好 - 它可以节省空间,但会使任何不必要的查询难以编写。
  • @Arvo,我无法控制设计,这是旧的设计风格,它符合要求。我只是尝试通过简化反向检查来使事情变得更容易

标签: sql sql-server tsql join


【解决方案1】:

不要对角色进行硬编码,而是使用连接。

SELECT
   *
FROM
   table2
LEFT JOIN
   table1
       ON table2.roles & table1.RoleEnumValue = 1

这将在每个角色的单独行中为每个用户提供角色。

我建议您停下来,因为这是适用于 SQL 和关系数据库的正确规范化数据结构。


但是,您可以将结果折叠到每个用户的一行中,所有角色都在一个字符串中。

这通常是个坏主意,而且会使后续的 SQL 变得更加困难......

SELECT
   table2.UserName,
   STRING_AGG(table1.Role, ', ')   AS all_roles
FROM
   table2
LEFT JOIN
   table1
       ON table2.roles & table1.RoleEnumValue = 1
GROUP BY
   table2.UserName

【讨论】:

  • 嗯谢谢你的逻辑,我不知道没有正确的 fk 和 id 有办法加入。如何在维恩图中识别这种查询?
  • 连接谓词只是布尔表达式。如果表达式的结果为 TRUE,则将这些行连接在一起。你可以在那里有任何布尔表达式,尽管它们越复杂,优化器使用索引等就越困难。至于你的最后一个问题,我不知道你在问什么......跨度>
  • 我确实尝试过使用 while 循环,你的第二种方法让它像个孩子玩。有时我无法想象如何加入这些数据,所以我尝试参考 sql 的维恩图 ["blog.codinghorror.com/a-visual-explanation-of-sql-joins/"]
【解决方案2】:

修复您的数据模型!在少数情况下,摆弄比特是最好的解决方案。相反,为角色创建一个参考表:

create table roles (
    role_id int identity(1, 1) primary key,
    role_name varchar(255)
);

然后创建一个users 表:

create table users (
    user_id int identity(1, 1), primary key,
    user_name varchar(255)
);

然后创建一个user_roles 表:

create table user_roles (
    user_role_id int identity(1, 1) primary key,
    user_id int references users(user_id),
    role_id int references roles(role_id)
)

你想要的查询是:

select u.user_name, string_agg(r.role_name, ',')
from users u join
     user_roles ur
     on ur.user_id = u.user_id join
     roles r
     on r.role_id = ur.role_id
group by u.user_name;

位摆弄有什么问题?

  • 它限制了您可以拥有的角色数量。
  • 这是一种可能没有必要的优化。
  • 搜索特定角色不能利用索引。

是的,它确实有一些优势。但是,SQL 具有非常强大的机制来表示多对多关系。如果您不想使用它们,那么您需要非常明确地解释为什么它们不够用。

【讨论】:

  • 即使接受的答案不同,这应该是正确的答案。
猜你喜欢
  • 2021-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-05
相关资源
最近更新 更多