【问题标题】:Complicated MySQL JOIN query复杂的 MySQL JOIN 查询
【发布时间】:2020-08-09 22:11:19
【问题描述】:

我正在为我的公司开发一个 HelpDesk 系统,其中有一系列相关表格,如下图所示:

主表是tickets,里面存放了ticket的基本数据,包括它的作者id_pers_empleado

最重要的表格是tickets_asignaciones,您可以在其中指明工单是分配给员工(我们将在此表格中称为代理)id_agente 还是分配给团队id_equipo

团队在tickets_equipos 表中定义,组成每个团队的代理在tickets_equipos_agentes 表中指示,该表关联tickets_equiposempleados 表。 我需要的是一个查询,可以让我得到所有的票...

  1. 作者是某个工人 X (WHERE tickets.id_pers_empleado = X)。
  2. 或者他们被直接分配给那个工人 X (WHERE tickets_asignaciones.id_pers_empleado = X)。
  3. 或者将他们分配到所述工人 X 所属的团队:
WHERE 
    tickets.id_pers_empleado IN (
        SELECT 
            tickets_equipos_agentes.id_pers_empleado 
        FROM 
            tickets_equipos_agentes 
        WHERE 
            tickets_asignaciones.id_equipo = tickets_equipos_agentes.id_pers_empleado
   )
 AND
   tickets.id_pers_empleado = X

(我不确定以上是否正确,可能不是)。

我可以通过使用 PHP 和 foreach 循环执行多个独立查询来做到这一点,但我不想让服务器因如此多的请求而超载,我需要一个能够同时完成所有工作的查询。

我已经以不同的方式尝试了很多 JOINS,现在我头晕目眩,卡住了。我找不到正确编写查询的正确方法。另外,我不知道是否应该以某种方式使用子查询来实现我想要做的事情。

我认为问题不在于表格的关系或布局,而在于我对此类复杂查询缺乏了解。

有人对如何解决它有任何想法吗?谢谢。


我将放置我正在使用的小数据集。

门票表

这里id_pers_empleado是工单的作者,即创建记录的用户。

+-------------+---------------------+----------------------------------------------+---------+------------------+
| id          | fecha               | titulo                                       | id_tipo | id_pers_empleado |
+=============+=====================+==============================================+=========+==================+
| 00000000001 | 2020/07/26 08:24:40 | Ticket de autoría propia (sin asignación)    | 01      | 00000005         |
+-------------+---------------------+----------------------------------------------+---------+------------------+
| 00000000002 | 2020/08/08 16:32:43 | Ticket asignado a un solo agente             | 02      | 00000011         |
+-------------+---------------------+----------------------------------------------+---------+------------------+
| 00000000003 | 2020/08/08 19:19:38 | Ticket asignado a un equipo                  | 02      | 00000012         |
+-------------+---------------------+----------------------------------------------+---------+------------------+
| 00000000004 | 2020/08/08 19:24:16 | Ticket asignado a varios agentes             | 02      | 00000004         |
+-------------+---------------------+----------------------------------------------+---------+------------------+
| 00000000005 | 2020/08/08 19:27:21 | Ticket asignado a un equipo y varios agentes | 01      | 00000001         |
+-------------+---------------------+----------------------------------------------+---------+------------------+

TICKETS_ASIGNACIONES 表

在此表中,id_pers_empleado 是分配票证的人,id_agente 是分配票证的人。有可能id_agente为空,此时必须填写id_equipo

这里id_pers_empleado 不能为空,因为它试图知道谁是分配/升级票证的人。特定工单也可能没有任何分配,因为该工单是新工单并且尚未决定应将其分配给谁,或者因为它是信息工单并且不需要任何代理干预。

+-------------+------------------+-----------+-----------+
| id_ticket   | id_pers_empleado | id_equipo | id_agente |
+=============+==================+===========+===========+
| 00000000002 | 00000011         |           | 00000005  |
+-------------+------------------+-----------+-----------+
| 00000000003 | 00000002         | 02        |           |
+-------------+------------------+-----------+-----------+
| 00000000004 | 00000003         |           | 00000003  |
+-------------+------------------+-----------+-----------+
| 00000000004 | 00000002         |           | 00000002  |
+-------------+------------------+-----------+-----------+
| 00000000005 | 00000001         | 04        |           |
+-------------+------------------+-----------+-----------+
| 00000000005 | 00000001         |           | 00000001  |
+-------------+------------------+-----------+-----------+
| 00000000005 | 00000001         |           | 00000004  |
+-------------+------------------+-----------+-----------+

TICKETS_EQUIPOS 表

它是tickets_asignaciones tickets_equipos_agentes之间的桥梁,它还会保存其他数据,例如图标,颜色,以及谁是团队负责人,但它们不相关,因此我没有包含它们。

+-------------+--------------+
| id          | nombre       |
+=============+==============+
| 00000000001 | Contabilidad |
+-------------+--------------+
| 00000000002 | RRHH         |
+-------------+--------------+
| 00000000003 | Calidad      |
+-------------+--------------+
| 00000000004 | Técnico      |
+-------------+--------------+

TICKETS_EQUIPOS_AGENTES 表

这里的id_pers_empleado 是上表中属于某个团队的工人。同一个工作人员/代理可以同时在多个团队中。

+-------------+------------------+
| id_equipo   | id_pers_empleado |
+=============+==================+
| 00000000001 | 00000001         |
+-------------+------------------+
| 00000000001 | 00000098         |
+-------------+------------------+
| 00000000002 | 00000006         |
+-------------+------------------+
| 00000000002 | 00000007         |
+-------------+------------------+
| 00000000002 | 00000008         |
+-------------+------------------+
| 00000000003 | 00000001         |
+-------------+------------------+
| 00000000003 | 00000011         |
+-------------+------------------+
| 00000000003 | 00000098         |
+-------------+------------------+
| 00000000004 | 00000005         |
+-------------+------------------+
| 00000000004 | 00000034         |
+-------------+------------------+

到目前为止的近似值

到目前为止,我发现显示我正在寻找的结果的最佳方式是:

SELECT
    t1.id,
    t1.id_pers_empleado autor,
    t1.titulo,
    t2.id_agente,
    t2.id_equipo,
    t3.nombre nombreEquipo
FROM
    tickets t1
LEFT JOIN
    tickets_asignaciones t2 ON t1.id = t2.id_ticket 
LEFT JOIN
    tickets_equipos t3 ON t2.id_equipo = t3.id
WHERE
    t1.id_pers_empleado = 5
OR
    t2.id_agente = 5
OR
    t1.id_pers_empleado IN (
        SELECT
            id_pers_empleado 
        FROM
            tickets_equipos_agentes t4
            LEFT JOIN tickets_equipos t5 ON t4.id_equipo = t5.id
        WHERE t4.id_equipo = t3.id
    )
GROUP BY t1.id
ORDER BY t1.id

运算结果集是这样的:

+-------------+----------+----------------------------------------------+-----------+-----------+--------------+
| id_ticket   | autor    | titulo                                       | id_agente | id_equipo | nombreEquipo |
+=============+==========+==============================================+===========+===========+==============+
| 00000000001 | 00000005 | Ticket de autoría propia (sin asignación)    |           |           |              |
+-------------+----------+----------------------------------------------+-----------+-----------+--------------+
| 00000000002 | 00000011 | Ticket asignado a un solo agente             | 5         |           |              |
+-------------+----------+----------------------------------------------+-----------+-----------+--------------+
| 00000000005 | 00000001 | Ticket asignado a un equipo y varios agentes |           | 4         | Técnico      |
+-------------+----------+----------------------------------------------+-----------+-----------+--------------+

到目前为止,它似乎有效,但我不知道我是否以最好的方式做到这一点,或者有什么可以从查询中改进的。还有一些情况我看不到正确的结果。我快疯了。

【问题讨论】:

标签: mysql database join


【解决方案1】:

我认为使用外部连接应该是这样的:

select *
from tickets t
left join tickets_asignaciones ta on ta.id_ticket = t.id
left join tickets_equipos_agentes tea on tea.id_equipo = ta.id_equipo
left join tickets_equipos te ON te.id = tea.id_equipo
where t.id_pers_empleado = 5 or ta.id_agente = 5 or tea.id_pers_empleado = 5
order by t.fecha;

WHERE 子句可以缩短为:

where 5 in (t.id_pers_empleado, ta.id_agente, tea.id_pers_empleado)

演示:https://dbfiddle.uk/?rdbms=oracle_18&fiddle=cfd9a1ee71542fbe57987355289dd946

【讨论】:

  • 我想知道为什么有人不赞成这个。虽然我看到我的答案没有太多解释,但我不知道我应该解释什么,因为查询很简单,应该很容易理解。我添加了一个演示来显示查询正在选择请求的数据。
  • 我认为您的回复确实比我到达的最终结果更好更快。我也做了一些测试,它工作得很好。在我的最终解决方案中,我找到了了解工作人员与工单(作者、直接分配、团队分配)关系的方法,这是我所需要的奖励,但你的回答是完全有效的,我可以在我的最终实施。我会标记为回复。谢谢!
【解决方案2】:

在我看来,您有 4 种可能性,我将它们联合起来。

1-Person 是工单的作者

select
        T.id,
        T.id_pers_empleado autor,
        T.Titulo,
        E.nombre
    from
        Tickets T
            JOIN Empleados E
                on T.id_pers_empleado = E.id
    where
        T.id_pers_empleado = 5

2-来自Tickets_Asignaciones,此人可能处于三个位置中的任何一个...... 第一个选项,它们是直接的 id_pers_empleado

select
        T.id,
        T.id_pers_empleado autor,
        T.Titulo,
        E.nombre
    from
        Tickets_Asignaciones TA
            JOIN Tickets T
                on TA.id_ticket = T.id
                JOIN Empleados E
                    on TA.id_pers_empleado = E.id
    where
        TA.id_pers_empleado = 5

3-来自Tickets_Asignaciones,此人可能处于三个位置中的任何一个...... 第二种选择,他们是直接代理

select
        T.id,
        T.id_pers_empleado autor,
        T.Titulo,
        E.nombre
    from
        Tickets_Asignaciones TA
            JOIN Tickets T
                on TA.id_ticket = T.id
                JOIN Empleados E
                    on TA.id_pers_empleado = E.id
    where
        TA.id_agente = 5

4-从Tickets_Asignaciones,该人可能处于3个位置中的任何一个...... 第三个选项,它们是通过equipos 关联的第二个关联。我们实际上可以跳过tickets_equipos,因为tickets_equipos_agentes 已经有问题的ID,我可以通过

select
        T.id,
        T.id_pers_empleado autor,
        T.Titulo,
        E.nombre
    from
        tickets_equipos_agentes TEA
            JOIN Tickets_Asignaciones TA
                on TEA.id_equipo = TA.id_equipo
                JOIN Tickets T
                    on TA.id_ticket = T.id
                    JOIN Empleados E
                        on TA.id_pers_empleado = E.id
    where
        TEA.id_pers_empleado = 5

因此,通过执行 UNION,如果某个查询中已经存在该值,则应该从任何其他查询中跳过该值,例如一个人既是作者,又是通过等价关联找到的。它应该只退票一次。 Union ALL 实际上会返回多个符合条件的每个查询的记录。此外,您可以执行 DISTINCT,这也可以防止出现同一张票。

[query 1]
UNION
[query 2]
UNION
[query 3]
UNION
[query 4]

为了帮助查询,Tickets 将在 ID 上有明显的索引。此外,在“ID_Pers_Empleado”上有一个索引。剩下的查询,我会确保 Tickets_Asignaciones 在 id_pers_empleado、id_equipo 和 id_agente 上有单独的索引...同样,每个单独的查询有 3 个索引,分别用于对它们进行连接优化。

【讨论】:

    【解决方案3】:

    通过不同的方法,我终于找到了一个可以正常工作的解决方案,我将在这里与其他程序员分享:

    SELECT * FROM (
        SELECT 
            'autor' relacion, -- added a "relation" field to get which case is occurring
            t.* 
        FROM 
            tickets t
        WHERE
            id_pers_empleado = $id_pers_empleado -- PHP var to set the desired id_pers_empleado
    UNION   
        SELECT 
            'asignado directamente',
            t.* 
        FROM 
            tickets t
            INNER JOIN tickets_asignaciones ta ON ta.id_ticket = t.id
        WHERE
            ta.id_agente = $id_pers_empleado -- PHP var to set the desired id_pers_empleado
    UNION
        SELECT
            'asignado al equipo',
            t.* 
        FROM 
            tickets t
            INNER JOIN tickets_asignaciones ta ON ta.id_ticket = t.id
            INNER JOIN tickets_equipos e ON e.id = ta.id_equipo
            INNER JOIN tickets_equipos_agentes ea ON ea.id_equipo = e.id
        WHERE
            ea.id_pers_empleado = $id_pers_empleado -- PHP var to set the desired id_pers_empleado
    ) sq1
    GROUP BY id -- grouping to avoid duplicated values on different cases occurring at once
    ORDER BY id -- could order by timestamp, but I prefer by ticket ID
    

    如果您认为有更好的实现,请告诉我。

    【讨论】:

      猜你喜欢
      • 2012-07-19
      • 2014-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多