2010-09-01 79 views
45

我有一个表顾客存储customer_id,电子邮件和参考。还有一个额外的表customer_data,用于存储对客户进行的更改的历史记录,即插入新行时发生更改。MySQL仅加入最近一行?

为了在表中显示客户信息,需要连接两个表,但只有customer_data中最近的一行应该连接到customer表。

由于查询是分页的,所以有一个限制和一个偏移量。

我该怎么用MySQL做到这一点?我想我希望把不同之处在那里的某个地方......

在一分钟的查询就像是这个 -

SELECT *, CONCAT(title,' ',forename,' ',surname) AS name 
FROM customer c 
INNER JOIN customer_data d on c.customer_id=d.customer_id 
WHERE name LIKE '%Smith%' LIMIT 10, 20 

Additionaly,我是正确的思维,我可以在使用CONCAT与LIKE这条路?

(我明白,INNER JOIN可能是错误的连接类型使用的。其实,我不知道有什么区别之间的不同连接。我要去看看现在!)

+0

客户是如何记录表是什么样子?最近的行如何确定?有时间戳字段吗? – 2010-09-01 14:25:17

+0

最近的只是插入的最后一行 - 所以它的主键是最高的数字。 – bcmcfc 2010-09-01 14:31:38

+0

为什么不触发?看看这个答案: http://stackoverflow.com/questions/26661314/best-and-optimal-way-to-join-max-value-from-other-table/26664982#26664982 – 2014-10-31 00:39:58

回答

76

你可能想尝试以下操作:

SELECT CONCAT(title, ' ', forename, ' ', surname) AS name 
FROM  customer c 
JOIN  (
       SELECT MAX(id) max_id, customer_id 
       FROM  customer_data 
       GROUP BY customer_id 
     ) c_max ON (c_max.customer_id = c.customer_id) 
JOIN  customer_data cd ON (cd.id = c_max.max_id) 
WHERE  CONCAT(title, ' ', forename, ' ', surname) LIKE '%Smith%' 
LIMIT  10, 20; 

注意,一个JOIN仅仅是INNER JOIN的代名词。

测试用例:

CREATE TABLE customer (customer_id int); 
CREATE TABLE customer_data (
    id int, 
    customer_id int, 
    title varchar(10), 
    forename varchar(10), 
    surname varchar(10) 
); 

INSERT INTO customer VALUES (1); 
INSERT INTO customer VALUES (2); 
INSERT INTO customer VALUES (3); 

INSERT INTO customer_data VALUES (1, 1, 'Mr', 'Bobby', 'Smith'); 
INSERT INTO customer_data VALUES (2, 1, 'Mr', 'Bob', 'Smith'); 
INSERT INTO customer_data VALUES (3, 2, 'Mr', 'Jane', 'Green'); 
INSERT INTO customer_data VALUES (4, 2, 'Miss', 'Jane', 'Green'); 
INSERT INTO customer_data VALUES (5, 3, 'Dr', 'Jack', 'Black'); 

结果(查询没有LIMITWHERE):

SELECT CONCAT(title, ' ', forename, ' ', surname) AS name 
FROM  customer c 
JOIN  (
       SELECT MAX(id) max_id, customer_id 
       FROM  customer_data 
       GROUP BY customer_id 
     ) c_max ON (c_max.customer_id = c.customer_id) 
JOIN  customer_data cd ON (cd.id = c_max.max_id); 

+-----------------+ 
| name   | 
+-----------------+ 
| Mr Bob Smith | 
| Miss Jane Green | 
| Dr Jack Black | 
+-----------------+ 
3 rows in set (0.00 sec) 
+1

感谢您进入那里的详细程度。我希望它能帮助别人,也能帮助我! – bcmcfc 2010-09-01 15:00:35

+6

从长远来看,这种方法可能会产生性能问题,因为它需要创建一个临时表。因此,另一个解决方案(如果可能的话)是在customer_data中添加一个新布尔字段(is_last),每次添加新条目时都必须更新它。最后一个条目将有is_last = 1,其他所有客户 - is_last = 0。 – cephuo 2014-09-19 11:50:24

+0

人们应该(请)也阅读以下答案(来自Danny Coulombe),因为这个答案(对不起丹尼尔)非常缓慢,查询时间更长/数据更多。让我的页面“等待”12秒加载;所以请检查https://stackoverflow.com/a/35965649/2776747。直到经过许多其他改变之后,我才注意到它,所以我花了很长时间才发现。 – Art 2018-02-26 22:59:46

0
SELECT CONCAT(title,' ',forename,' ',surname) AS name * FROM customer c 
INNER JOIN customer_data d on c.id=d.customer_id WHERE name LIKE '%Smith%' 

我认为你需要改变 c.customer_id到c.id

其他更新表结构

+0

我已经downvoted因为我误解了你的答案,我最初认为这是错误的。急速是一个不好的顾问:-) – Wirone 2015-03-09 13:09:52

0

这是一个很好的想法,记录实际数据为“CUSTOMER_DATA”表。有了这些数据,您可以根据需要从“customer_data”表中选择所有数据。

10

。假定在customer_data自动增量列被命名为Id,你可以这样做:

SELECT CONCAT(title,' ',forename,' ',surname) AS name * 
FROM customer c 
    INNER JOIN customer_data d 
     ON c.customer_id=d.customer_id 
WHERE name LIKE '%Smith%' 
    AND d.ID = (
       Select Max(D2.Id) 
       From customer_data As D2 
       Where D2.customer_id = D.customer_id 
       ) 
LIMIT 10, 20 
7

对于任何必须使用旧版本的MySQL(5.0之前版本)您无法对此类查询执行子查询。这是我能够做的解决方案,它似乎工作得很好。

SELECT MAX(d.id), d2.*, CONCAT(title,' ',forename,' ',surname) AS name 
FROM customer AS c 
LEFT JOIN customer_data as d ON c.customer_id=d.customer_id 
LEFT JOIN customer_data as d2 ON d.id=d2.id 
WHERE CONCAT(title, ' ', forename, ' ', surname) LIKE '%Smith%' 
GROUP BY c.customer_id LIMIT 10, 20; 

本质上讲,这是找到你的数据表的最大ID其连接到客户,然后加入数据表中发现的最大的ID。原因是因为选择组的最大值并不能保证其余数据与该id匹配,除非您将其重新加入到自身。

我还没有在较新版本的MySQL上测试过它,但它在4.0.30上有效。

+0

这是它的简洁精致。为什么这是我第一次见到这种方法?请注意,'EXPLAIN'表示这使用临时表和文件。在末尾添加'ORDER BY NULL'会清除文件。 – Timo 2015-12-04 09:11:52

+0

令我遗憾的是,我自己的,不那么漂亮的解决方案是我数据的3.5倍。我使用子查询来选择主表加上最近的连接表的ID,然后使用外部查询来选择子查询并从连接的表中读取实际数据。我将5个表格连接到主表格上,并使用where条件来选择1000条记录进行测试。索引是最佳的。 – Timo 2015-12-04 09:14:50

+0

我正在用'SELECT *,MAX(firstData.id),MAX(secondData.id)[']''使用你的解决方案。逻辑上,通过改变为'SELECT main。*,firstData2。*,secondData2。*,MAX(firstData.id),MAX(secondData.id),[']我能够使其显着更快。这允许第一个连接只从索引读取,而不必从主索引读取所有数据。现在,这个漂亮的解决方案只需要基于子查询的解决方案的1.9倍。 – Timo 2015-12-04 09:21:24

31

如果您正在处理繁重的查询,最好移动where子句中最新一行的请求。它速度更快,看起来更干净。

SELECT c.*, 
FROM client AS c 
LEFT JOIN client_calling_history AS cch ON cch.client_id = c.client_id 
WHERE 
    cch.cchid = (
     SELECT MAX(cchid) 
     FROM client_calling_history 
     WHERE client_id = c.client_id AND cal_event_id = c.cal_event_id 
    ) 
+3

哇,我几乎不相信这是多少性能差异。不知道为什么这么激烈,但到目前为止,它更快,它感觉就像我在其他地方搞砸了... – 2017-03-21 20:53:49

+1

我真的希望我能不止一次地+1这样看到更多。我已经测试了这一点,不知何故它使我的查询几乎是瞬间的(即使使用'sql_no_cache set',WorkBench的字面意思是0.000秒),而在联接中执行搜索需要几秒钟才能完成。仍然困惑,但我的意思是你不能与这样的结果争论。 – 2017-03-21 21:27:06

+0

我不确定为什么它更快,但我听说MySQL总是从底部开始。所以可能是因为ID索引,而不是每次放入JOIN时都重复,“SELECT MAX”查询只是继续它的位置,并且只运行几行。 – 2017-03-30 12:14:17

0

你也可以做到这一点

SELECT CONCAT(title, ' ', forename, ' ', surname) AS name 
FROM  customer c 
LEFT JOIN (
       SELECT * FROM customer_data ORDER BY id DESC 
     ) customer_data ON (customer_data.customer_id = c.customer_id) 
GROUP BY c.customer_id   
WHERE  CONCAT(title, ' ', forename, ' ', surname) LIKE '%Smith%' 
LIMIT  10, 20;