2010-09-01 76 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;