[英]Complicated MySQL JOIN query
我正在为我的公司开发一个 HelpDesk 系统,其中有一系列相关表格,如下图所示:
主表是tickets
,我存储tickets的基本数据,包括它的作者id_pers_empleado
。
最重要的表是tickets_asignaciones
,您可以在其中指明工单是分配给员工(我们将在此表中称为代理) id_agente
还是分配给团队id_equipo
。
团队在tickets_equipos
表中定义,组成每个团队的代理在tickets_equipos_agentes
表中指示,该表关联tickets_equipos
和empleados
表。 我需要的是一个查询,可以让我得到所有的票......
WHERE tickets.id_pers_empleado = X
)。WHERE tickets_asignaciones.id_pers_empleado = 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 |
+-------------+---------------------+----------------------------------------------+---------+------------------+
在此表中, 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_asignaciones
和tickets_equipos_agentes
之间的桥梁,它还会保存其他数据,例如图标、颜色和谁是团队负责人,但它们不相关,所以我没有包含它们。
+-------------+--------------+
| id | nombre |
+=============+==============+
| 00000000001 | Contabilidad |
+-------------+--------------+
| 00000000002 | RRHH |
+-------------+--------------+
| 00000000003 | Calidad |
+-------------+--------------+
| 00000000004 | Técnico |
+-------------+--------------+
这里的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.