【问题标题】:How to check if a particular mapping exists in a one-to-many mapping table如何检查一对多映射表中是否存在特定映射
【发布时间】:2016-03-19 18:52:57
【问题描述】:

我有一个表,该表维护EMPLOYEE_ID 到可以分配给员工的一个或多个ROLE_IDs 的映射。 ROLE_IDROLE 表的主键。

现在,我正在尝试查找特定员工是否是团队负责人 (ROLE_ID = 2)。也就是本质上是试图找出映射表中是否存在(EMPLOYEE_ID, 2)的特定映射组合。

目前,我正在使用以下查询来实现此目的:

SELECT E.NAME AS `EMPLOYEE_NAME`, 
 EXISTS( SELECT 1 FROM `EMPLOYEE_ROLE` WHERE 
   (`EMPLOYEE_ROLE`.`EMPLOYEE_ID` = `E`.`EMPLOYEE_ID`)
   AND (`EMPLOYEE_ROLE`.`ROLE_ID` = 2)) AS `IS_TEAM_LEADER`
 -- Assume some other column shall be selected from ER table, 
 -- hence necessitating the JOIN on ER
FROM EMPLOYEE E
JOIN EMPLOYEE_ROLE ER ON (ER.EMPLOYEE_ID = E.EMPLOYEE_ID)
GROUP BY E.EMPLOYEE_ID;

虽然这似乎可以完成工作,但我正在寻找一种更有效的方法,因为当前形式的子查询似乎是多余的。不确定是否相关,但可以使用FIND_IN_SET 或类似的功能吗?

谁能提出解决方案,因为我对性能最佳的方法感兴趣?

编辑1:我有意使用JOIN EMPLOYEE_ROLE,目的是也可以从ER 表中选择其他列。所以,我正在寻找优化子查询,同时保持连接不变。因此,语句“当前形式的当前子查询似乎是多余的”。

SQLFiddle:http://sqlfiddle.com/#!9/2aad3/5

【问题讨论】:

  • 我想你的意思是ROLE_ID = 2,而不是EMPLOYEE_ID = 2

标签: mysql query-performance


【解决方案1】:

使用exists子查询或使用join,但你不应该在一个查询中同时使用。

我会使用连接方法,因为如果需要,很容易获得与角色相关的数据:

SELECT E.NAME AS `EMPLOYEE_NAME`, 
FROM EMPLOYEE E
INNER JOIN EMPLOYEE_ROLE ER ON (ER.EMPLOYEE_ID = E.EMPLOYEE_ID)
WHERE ER.ROLE_ID=2;

如果您需要所有员工的列表,其中包含指示该员工是否为 IS 领导者的字段,请使用左连接而不是内部:

SELECT DISTINCT E.NAME AS `EMPLOYEE_NAME`, IF(ER.ROLE_ID IS NULL, 'NOT IS Leader','IS Leader') AS IsISLeader
FROM EMPLOYEE E
LEFT JOIN EMPLOYEE_ROLE ER ON ER.EMPLOYEE_ID = E.EMPLOYEE_ID AND ER.ROLE_ID=2;

【讨论】:

  • 假设员工不能两次成为领导者,第二个查询中的 DISTINCT 可能是多余的。不过,我希望 (employee_id, role_id) 上有一个 UNIQUE。此外,来自 OP 小提琴的布尔 is_team_leader 字段可以简单地定义为 ER.ROLE_ID IS NOT NULL 没有所有 IF 的东西。
  • 一个员工可能有多个角色,因此是不同的。由于我们都不知道这家公司是如何运作的,也不知道这个作业中需要哪些数据,所以我把不同的地方留在那里。 IF() 用于格式化输出。
  • 是的,已经被您的 LEFT JOIN 过滤的多行,关闭 DISTINCT 的唯一问题是,如果员工可以被列为 team_leader 两次(这很有可能不会)规范中不允许)..由于这被标记为性能问题,我会非常清楚地说明这一点。因此,如果 OP 遵循 Rick James 回答中的建议,则可以删除 DISTINCT。
  • 您的IF() 格式令人困惑。包含'NOT IS Leader''IS Leader' 的名为IsISLeader 的列太可怕了。 OP 的小提琴在名为is_team_leader 的列中将其作为简单的 1 或 0,这要简单得多..为什么要通过增加可读性较差的语法来使这个复杂化?
  • 你在认真地捍卫一个名为'IsISLeader'的专栏?这几乎表明这些是为 Daesh 工作的员工!查询结构和数据操作中的意见并非生而平等。看看 OP 的小提琴,如果你故意改变这样的东西,你至少应该有一个正当的理由。
【解决方案2】:

“表现最佳”--

CREATE TABLE `EMPLOYEE_ROLE` (
  `EMPLOYEE_ID` INT NOT NULL,
  `ROLE_ID` INT NOT NULL,
  PRIMARY KEY(`EMPLOYEE_ID`, ROLE_ID),
  INDEX(`ROLE_ID` EMPLOYEE_ID)
) ENGINE=InnoDB;

Why.

除此之外,请参阅@Shadow 的回答。

【讨论】:

    【解决方案3】:

    我在 SQL 中包含 EMPLOYEE_ID 以防为不同的 EMPLOYEE_ID 找到相同的名称,如果某些员工没有角色,也使用 LEFT JOIN:

    SELECT E.EMPLOYEE_ID, E.NAME, SUM(IF(R.ROLE_ID=2,1,0)) IS_TEAM_LEADER
    FROM EMPLOYEE E
    LEFT JOIN EMPLOYEE_ROLE R ON E.EMPLOYEE_ID = R.EMPLOYEE_ID
    GROUP BY 1,2
    

    【讨论】:

      【解决方案4】:

      只需使用 FK 关系 创建一个角色表

      table role{
      RoleID
      RoleName
      Status
      ...}
      

      并将员工表编辑为

      table employee{
      ID
      name
      ...
      }
      

      创建表员工角色

      table employeerole{
      ID
      roleID
      employeeID
      

      }

      现在您也可以使用规范化规则为每个员工管理多个角色

      现在你可以使用查询了

      SELECT employee.ID, employee.Name, employeerole.roleid FROM role 
      INNER   JOIN employeerole ON role.RoleID = employeerole.roleid INNER
      JOIN   dbo.employee ON dbo.employeerole.employeeid = dbo.employee.ID 
      WHERE   employeerole.roleid = 2
      

      表角色的 ID 为 2,名称例如是团队负责人或经理

      现在这个查询已经完全优化,会给你最好的结果

      【讨论】:

      • 员工可以有一个或多个角色。因此,将角色包含在员工表中并不是真正理想的设计。它违反了规范化原则。
      • 请详细说明如何利用您提出的建议来提高给定查询的性能。
      【解决方案5】:

      如果我对您的理解正确,您正在尝试获取所有员工及其所有角色,以及一个额外的列,表明他们是否是团队负责人。如果是这样,您只需要加入 EMPLOYEE_ROLE 两次,第二次是 LEFT JOIN 以检查特定角色:

      SELECT E.NAME, ER.ROLE_ID, ER2.EMPLOYEE_ID IS NOT NULL AS `IS_TEAM_LEADER`
        FROM EMPLOYEE E
        JOIN EMPLOYEE_ROLE ER ON ER.EMPLOYEE_ID = E.EMPLOYEE_ID
        LEFT JOIN EMPLOYEE_ROLE ER2 ON ER2.EMPLOYEE_ID = E.EMPLOYEE_ID AND ER2.ROLE_ID = 2;
      

      SQL 小提琴:http://sqlfiddle.com/#!9/2aad3/9/0

      【讨论】:

        【解决方案6】:

        这是获取属于特定角色的所有员工的查询

        SELECT e.NAME FROM EMPLOYEE e right join (SELECT EMPLOYEE_ID FROM
        EMPLOYEE_ROLE WHERE ROLE_ID=2) er using(EMPLOYEE_ID);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-09-13
          • 2012-01-11
          • 2019-08-20
          • 1970-01-01
          • 1970-01-01
          • 2021-12-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多