Mysql OneToMany 只加入最近的记录

Posted

技术标签:

【中文标题】Mysql OneToMany 只加入最近的记录【英文标题】:Mysql OneToMany JOIN most recent record only 【发布时间】:2018-04-23 06:43:03 【问题描述】:

我有两张桌子customers 和它们的contacts。一个客户可以有许多联系方式。在这种情况下,我只需要为客户获取最后添加的联系方式。我可以通过subquery 来实现。但是当数据量很大时,我在查询所有客户数据时面临性能不足的问题。

客户表(customers_customers)

+-------------------+--------------+------+-----+---------+----------------+
| Field             | Type         | Null | Key | Default | Extra          |
+-------------------+--------------+------+-----+---------+----------------+
| id                | int(11)      | NO   | PRI | NULL    | auto_increment |
| company_name      | varchar(150) | NO   |     | NULL    |                |
| logo              | varchar(100) | NO   |     | NULL    |                |
+-------------------+--------------+------+-----+---------+----------------+

联系人表 (customers_customercontacts)

+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| email       | varchar(100) | YES  |     | NULL    |                |
| mobile      | varchar(50)  | YES  |     | NULL    |                |
| customer_id | int(11)      | NO   | MUL | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+

我尝试了以下查询,我得到了结果,但是查询很慢。

SELECT
    c.id,
    c.company_name,
    d.mobile
FROM customers_customers AS c
LEFT JOIN customers_customercontacts AS d
    ON d.id = (SELECT MAX(id) FROM customers_customercontacts WHERE d.customer_id = d.id);

SELECT
    c.id,
    c.company_name,
    d.mobile
FROM customers_customers AS c
LEFT JOIN customers_customercontacts AS d
    ON d.id = (SELECT id FROM customers_customercontacts WHERE d.customer_id = d.id
               ORDER BY id DESC LIMIT 1);

我需要获取客户的公司名称和每个公司名称的最后添加的电话号码。 是否有任何优化的方式或不使用子查询的方式来实现这一目标?

已解决

非相关子查询的性能总是优于相关子查询。

【问题讨论】:

你能explain你的查询吗? 我需要获取客户公司名称和他们的电话号码。每个客户只有一个电话号码,应该是最后添加的一个。 Bill Karwin 的回答可能会对您有所帮助 Retrieving the last record in each group @Rajez 不,我的意思是使用解释语法,表可能没有优化,应该有一个索引 最佳groupwise max 【参考方案1】:

对子查询使用第二个连接,以标识每个客户的最近联系记录:

SELECT
    c.id,
    c.company_name,
    d1.mobile
FROM customers_customers AS c
LEFT JOIN customers_customercontacts AS d1
    ON c.id = d1.customer_id
INNER JOIN
(
    SELECT customer_id, MAX(id) AS max_id
    FROM customers_customercontacts
    GROUP BY customer_id
) AS d2
    ON d1.customer_id = d2.customer_id AND
       d1.id = d2.max_id;

这是对您的第一次查询尝试的修改。请注意,您的联接条件已损坏,因为它并没有真正正确地关联客户和联系人表。基本连接应该是customers_customers.id 匹配到customers_customercontacts.customer_id。除此更正外,我还进行了一次额外的联接,以限制每个客户最近的联系记录。

与原来的方法相比,此方法可能会提高性能的一个原因是此查询使用非相关子查询来查找每个客户的最新联系人。您最初的尝试使用了往往表现不佳的相关子查询。

【讨论】:

谢谢。我看到查询持续时间有了很好的改善。你能解释一下这种变化是如何提高性能的吗? @Rajez 我添加了解释。

以上是关于Mysql OneToMany 只加入最近的记录的主要内容,如果未能解决你的问题,请参考以下文章

加入表@OneToMany / @ManyToOne

Symfony -- 对 oneToMany 关系中的子记录进行排序

不加入子表的 JPA (@OneToMany) 查询

加入获取嵌套在 @OneToMany 中的 @ManyToOne

仅在 MySQL 中返回最近 3 个月的记录

MySQL:如何更新时间最近的一条记录