MySQL的:我可以做一个左连接和连接表只拉一排?MySQL的:我可以做一个左连接和连接表只拉一排?

2019-06-02 15:47发布

我写了工作的自定义帮助台,它是伟大运行......直到最近。 一个查询确实放缓。 现在只需约14秒! 以下是相关的表:

CREATE TABLE `tickets` (
  `id` int(11) unsigned NOT NULL DEFAULT '0',
  `date_submitted` datetime DEFAULT NULL,
  `date_closed` datetime DEFAULT NULL,
  `first_name` varchar(50) DEFAULT NULL,
  `last_name` varchar(50) DEFAULT NULL,
  `email` varchar(50) DEFAULT NULL,
  `description` text,
  `agent_id` smallint(5) unsigned NOT NULL DEFAULT '1',
  `status` smallint(5) unsigned NOT NULL DEFAULT '1',
  `priority` tinyint(4) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `date_closed` (`date_closed`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `solutions` (
  `id` int(10) unsigned NOT NULL,
  `ticket_id` mediumint(8) unsigned DEFAULT NULL,
  `date` datetime DEFAULT NULL,
  `hours_spent` float DEFAULT NULL,
  `agent_id` smallint(5) unsigned DEFAULT NULL,
  `body` text,
  PRIMARY KEY (`id`),
  KEY `ticket_id` (`ticket_id`),
  KEY `date` (`date`),
  KEY `hours_spent` (`hours_spent`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

当用户提交了票,就进入了“入场券”表。 然后,随着代理商通过对问题的工作,他们记录他们所采取的行动。 每个条目将进入“解决方案”表。 换句话说,门票有许多解决方案。

已经放慢了查询的目标是把所有的字段从“票”表中,也从“方案”表的最新条目。 这是我一直在使用查询:

SELECT tickets.*,
    (SELECT CONCAT_WS(" * ", DATE_FORMAT(solutions.date, "%c/%e/%y"), solutions.hours_spent, CONCAT_WS(": ", solutions.agent_id, solutions.body))
    FROM solutions
    WHERE solutions.ticket_id = tickets.id
    ORDER BY solutions.date DESC, solutions.id DESC
    LIMIT 1
) AS latest_solution_entry
FROM tickets
WHERE tickets.date_closed IS NULL
OR tickets.date_closed >= '2012-06-20 00:00:00'
ORDER BY tickets.id DESC

这里的“latest_solution_entry”现场是什么样子的例子:

6/20/12 * 1337 * 1: I restarted the computer and that fixed the problem. Yes, I took an hour to do this.

在PHP中,我分裂了“latest_solution_entry”字段和正确格式化。

当我注意到,在运行查询的页面已经一路下滑放缓,我不跑了子查询的查询,这是超级快。 然后我跑的EXPLAIN对原始查询并得到了这一点:

+----+--------------------+-----------+-------+---------------+-----------+---------+---------------------+-------+-----------------------------+
| id | select_type        | table     | type  | possible_keys | key       | key_len | ref                 | rows  | Extra                       |
+----+--------------------+-----------+-------+---------------+-----------+---------+---------------------+-------+-----------------------------+
|  1 | PRIMARY            | tickets   | index | date_closed   | PRIMARY   | 4       | NULL                | 35804 | Using where                 |
|  2 | DEPENDENT SUBQUERY | solutions | ref   | ticket_id     | ticket_id | 4       | helpdesk.tickets.id |     1 | Using where; Using filesort |
+----+--------------------+-----------+-------+---------------+-----------+---------+---------------------+-------+-----------------------------+

所以我在寻找一种方法,使我的查询效率更高,但仍然达到同样的目的。 有任何想法吗?

Answer 1:

让我总结一下我的理解:你想选择每个售票和它的最后解决方案。

我喜欢使用这种问题下面的模式,因为它避免了子查询模式,需要的性能,其中因此,相当不错。 其缺点是,它是一个有点棘手理解:

SELECT
  t.*,
  s1.*
FROM tickets t
INNER JOIN solutions s1 ON t.id = s1.ticket_id
LEFT JOIN solutions s2 ON s1.ticket_id = s2.ticket_id AND s2.id > s1.id
WHERE s2.id IS NULL;

我写了一个更好的理解模式的唯一的心脏。

键是:

  • 在LEFT JOIN中的solutions表本身就带有s1.ticket_id = s2.ticket_id条件:它模拟了GROUP BY ticket_id

  • 条件s2.id > s1.id :它是“我只希望最后的解决方案”的SQL,它模拟了MAX() 我以为,在你的模型, the last是指with the greatest id ,但你可以在这里使用的日期的条件。 需要注意的是s2.id < s1.id会给你的第一个解决方案。

  • WHERE子句s2.id IS NULL :最奇怪的一个,但绝对必要的......只保留所需的记录。

有一个尝试,让我知道:)

编辑1:我刚刚意识到,第二点假设是过于简单化的问题。 这使得它更有趣:对我想看看这个模式可能与你的工作date, id顺序。

编辑2:好了,它有一点点扭曲的伟大工程。 左侧的条件JOIN变为:

LEFT JOIN solutions s2 ON s1.ticket_id = s2.ticket_id
  AND (s2.date > s1.date OR (s2.date = s1.date AND s2.id > s1.id))


Answer 2:

当你有SELECT子句中内嵌视图,必须执行选择的每一行。 我发现它的情况下更好地这样把内嵌视图FROM子句中,而不是将执行一次选择。

SELECT t.*, 
       Concat_ws(" * ", Date_format(s.date, "%c/%e/%y"), s.hours_spent, 
       Concat_ws(":", s.agent_id, s.body)) 
FROM   tickets t 
       INNER JOIN (SELECT solutions.ticket_id,
                          Max(solutions.date) maxdate 
                   FROM   solutions 
                   GROUP  BY solutions.ticket_id) last_solutions 
               ON t.id = last_solutions.ticket_id
       INNER JOIN (SELECT solutions.ticket_id,
                          solutions.date,
                          Max(solutions.id) maxid 
                   FROM   solutions 
                   GROUP  BY solutions.ticket_id,
                            solutions.date) last_solution
              ON last_solutions.ticket_id = last_solution.ticket_id 
                 and last_solutions.maxDate = last_solution.Date
       INNER JOIN solutions s 
               ON last_solution.maxid = s.id
WHERE  t.date_closed IS NULL 
        OR t.date_closed >= '2012-06-20 00:00:00' 
ORDER  BY t.id DESC 

注意:您可能需要让一个LEFT JOIN根据您的需要



Answer 3:

试试这个:

SELECT *
FROM (
  -- for each ticket get the most recent solution date
  SELECT ticket_id, MAX(solutions.date) as date
  FROM solutions
  GROUP BY ticket_id
) t
JOIN tickets ON t.ticket_id = tickets.id
WHERE tickets.date_closed IS NULL OR tickets.date_closed >= '2012-06-20 00:00:00'
ORDER BY tickets.id DESC

注意,如果没有与同日2个解决方案的票,你会在你的结果集重复的记录。 您将需要另一加入删除这些重复或使用绝对的序列,例如串行(递增的主键)。



Answer 4:

根据使用目的,我给一个想法:

SELECT DISTINCT s1.ticket_id, t.*,  s1.*
FROM tickets t
LEFT JOIN solutions s1 ON t.id = s1.ticket_id


文章来源: MySQL: Can I do a left join and pull only one row from the join table?