繁体   English   中英

复杂的 MySQL JOIN 查询

[英]Complicated MySQL JOIN query

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

塔布拉斯关系

主表是tickets ,我存储tickets的基本数据,包括它的作者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_asignacionestickets_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      |
+-------------+----------+----------------------------------------------+-----------+-----------+--------------+

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

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

1-人是票的作者

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,该人可以处于3个位置中的任何一个......第一个选项,他们是直接的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,该人可以处于3个位置中的任何一个......第二个选项,他们是直接代理

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 个索引,分别用于对它们进行连接优化。

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM