2017-02-17 68 views
0

我有3個表:如何選擇MariaDB中每個組的最新成員?

  1. 賬戶 - 賬戶信息
  2. 機 - 機資訊
  3. account_machine - 帳戶映射到一臺機器上

每個帳戶由一個處理的日期機。隨着時間的推移,一個帳戶可以遷移到不同的機器上,但在某一天它只能由一臺機器來處理。如果帳戶不再有效,那麼相應的machine_id爲0。給定一個日期,我想找到所有活躍賬戶,所以我想出了這個查詢:

SELECT account.id 
FROM account JOIN account_machine m 
ON m.account_id=account.id && m.machine_id && m.machine_id= 
(SELECT machine_id 
FROM account_machine 
WHERE account_id=account.id && date<=20170215 
ORDER BY date DESC LIMIT 1) 
GROUP BY account.id; 

這正常工作與MySQL,但沒有按」跟MariaDB一起。

MariaDB [db]> select * from account_machine; 
+------------+------------+------------+ 
| date  | account_id | machine_id | 
+------------+------------+------------+ 
| 2013-01-01 |   1 |   1 | 
| 2013-01-01 |   8 |   1 | 
| 2013-01-01 |   2 |   2 | 
| 2013-01-01 |   3 |   2 | 
| 2013-01-01 |   4 |   3 | 
| 2013-01-01 |   12 |   3 | 
| 2016-04-01 |   24 |   3 | 
| 2013-01-01 |   5 |   5 | 
| 2013-01-01 |   6 |   8 | 
| 2013-01-01 |   7 |   6 | 
| 2014-01-01 |   9 |   6 | 
| 2013-01-01 |   10 |   4 | 
| 2014-07-01 |   11 |   10 | 
| 2014-01-01 |   13 |   7 | 
| 2014-01-01 |   14 |   7 | 
| 2014-07-01 |   15 |   11 | 
| 2014-07-01 |   16 |   14 | 
| 2014-07-01 |   17 |   12 | 
| 2015-01-01 |   18 |   13 | 
| 2015-01-01 |   19 |   13 | 
| 2015-04-01 |   20 |   13 | 
| 2015-04-01 |   21 |   7 | 
| 2015-04-01 |   22 |   13 | 
| 2016-04-01 |   23 |   15 | 
| 2016-05-01 |   25 |   9 | 
| 2016-05-19 |   26 |   4 | 
| 2014-08-06 |   1 |   0 | 
| 2016-01-15 |   12 |   0 | 
| 2015-11-04 |   19 |   12 | 
| 2016-05-23 |   10 |   0 | 
| 2016-05-26 |   2 |   18 | 
| 2016-05-27 |   13 |   16 | 
| 2016-06-02 |   27 |   3 | 
| 2016-06-02 |   4 |   0 | 
| 2016-06-08 |   28 |   17 | 
| 2016-06-21 |   29 |   19 | 
| 2016-07-11 |   30 |   20 | 
| 2016-08-15 |   13 |   0 | 
| 2016-08-19 |   2 |   18 | 
| 2016-08-25 |   31 |   21 | 
| 2016-09-08 |   32 |   20 | 
| 2016-11-30 |   19 |   12 | 
| 2016-11-30 |   22 |   13 | 
| 2017-01-20 |   33 |   15 | 
+------------+------------+------------+ 

MariaDB [db]> select account.id from account join account_machine m on m.account_id=account.id && m.machine_id && m.machine_id=(select a.machine_id from account_machine a where a.account_id=account.id && a.date<=20170215 order by a.date desc limit 1) group by account.id; 
+----+ 
| id | 
+----+ 
| 23 | 
| 33 | 
+----+ 

mysql> select account.id from account join account_machine m on m.account_id=account.id && m.machine_id && m.machine_id=(select a.machine_id from account_machine a where a.account_id=account.id && a.date<=20170215 order by a.date desc limit 1) group by account.id; 
+----+ 
| id | 
+----+ 
| 2 | 
| 3 | 
| 5 | 
| 6 | 
| 7 | 
| 8 | 
| 9 | 
| 11 | 
| 14 | 
| 15 | 
| 16 | 
| 17 | 
| 18 | 
| 19 | 
| 20 | 
| 21 | 
| 22 | 
| 23 | 
| 24 | 
| 25 | 
| 26 | 
| 27 | 
| 28 | 
| 29 | 
| 30 | 
| 31 | 
| 32 | 
| 33 | 
+----+ 

P.S.這裏有3個表,爲您重現:

CREATE TABLE `account` (
    `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM; 
INSERT INTO `account` VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26),(27),(28),(29),(30),(31),(32),(33); 

CREATE TABLE `account_machine` (
    `date` date NOT NULL, 
    `account_id` smallint(5) unsigned NOT NULL, 
    `machine_id` smallint(5) unsigned NOT NULL, 
    PRIMARY KEY (`date`,`account_id`) 
) ENGINE=MyISAM; 
INSERT INTO `account_machine` VALUES ('2013-01-01',1,1),('2013-01-01',8,1),('2013-01-01',2,2),('2013-01-01',3,2),('2013-01-01',4,3),('2013-01-01',12,3),('2016-04-01',24,3),('2013-01-01',5,5),('2013-01-01',6,8),('2013-01-01',7,6),('2014-01-01',9,6),('2013-01-01',10,4),('2014-07-01',11,10),('2014-01-01',13,7),('2014-01-01',14,7),('2014-07-01',15,11),('2014-07-01',16,14),('2014-07-01',17,12),('2015-01-01',18,13),('2015-01-01',19,13),('2015-04-01',20,13),('2015-04-01',21,7),('2015-04-01',22,13),('2016-04-01',23,15),('2016-05-01',25,9),('2016-05-19',26,4),('2014-08-06',1,0),('2016-01-15',12,0),('2015-11-04',19,12),('2016-05-23',10,0),('2016-05-26',2,18),('2016-05-27',13,16),('2016-06-02',27,3),('2016-06-02',4,0),('2016-06-08',28,17),('2016-06-21',29,19),('2016-07-11',30,20),('2016-08-15',13,0),('2016-08-19',2,18),('2016-08-25',31,21),('2016-09-08',32,20),('2016-11-30',19,12),('2016-11-30',22,13),('2017-01-20',33,15); 

CREATE TABLE `machine` (
    `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM; 
INSERT INTO `machine` VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22); 
+0

是表已在兩種模式中相同的數據? –

+0

順便說一句'和m.machine_id'總是如此。如果你不屑於格式化您的疑問,這將是顯而易見的,你太 – Strawberry

+0

見http://meta.stackoverflow.com/questions/333952/why-should-i-provide-an-mcve-for-what-seems-to-我將是一個非常簡單的sql查詢 – Strawberry

回答

1

什麼這樣的事情?

SELECT am1.account_id AS id 
FROM account_machine am1 
JOIN (
    SELECT account_id, MAX(date) AS date 
    FROM account_machine 
    GROUP BY account_id 
    ) am2 
ON am1.account_id = am2.account_id 
AND am1.date = am2.date 
AND am1.machine_id != 0 
ORDER BY am1.account_id; 

+----+ 
| id | 
+----+ 
| 2 | 
| 3 | 
| 5 | 
| 6 | 
| 7 | 
| 8 | 
| 9 | 
| 11 | 
| 14 | 
| 15 | 
| 16 | 
| 17 | 
| 18 | 
| 19 | 
| 20 | 
| 21 | 
| 22 | 
| 23 | 
| 24 | 
| 25 | 
| 26 | 
| 27 | 
| 28 | 
| 29 | 
| 30 | 
| 31 | 
| 32 | 
| 33 | 
+----+ 
28 rows in set (0.00 sec) 

我很想知道從MySQL和MariaDB中看到EXPLAIN EXTENDED/SHOW WARNINGS的輸出。這將向您顯示查詢優化器是如何重寫查詢的。例如:

[email protected] [stack]> EXPLAIN EXTENDED SELECT am1.account_id AS id 
    -> FROM account_machine am1 
    -> JOIN (
    ->  SELECT account_id, MAX(date) AS date 
    ->  FROM account_machine 
    ->  GROUP BY account_id 
    ->) am2 
    -> ON am1.account_id = am2.account_id 
    -> AND am1.date = am2.date 
    -> AND am1.machine_id != 0 
    -> ORDER BY am1.account_id\G 
*************************** 1. row *************************** 
      id: 1 
    select_type: PRIMARY 
     table: <derived2> 
     type: ALL 
possible_keys: NULL 
      key: NULL 
     key_len: NULL 
      ref: NULL 
     rows: 44 
    filtered: 100.00 
     Extra: Using where; Using temporary; Using filesort 
*************************** 2. row *************************** 
      id: 1 
    select_type: PRIMARY 
     table: am1 
     type: eq_ref 
possible_keys: PRIMARY 
      key: PRIMARY 
     key_len: 5 
      ref: am2.date,am2.account_id 
     rows: 1 
    filtered: 100.00 
     Extra: Using where 
*************************** 3. row *************************** 
      id: 2 
    select_type: DERIVED 
     table: account_machine 
     type: index 
possible_keys: NULL 
      key: PRIMARY 
     key_len: 5 
      ref: NULL 
     rows: 44 
    filtered: 100.00 
     Extra: Using index; Using temporary; Using filesort 
3 rows in set, 1 warning (0.00 sec) 

[email protected] [stack]> SHOW WARNINGS\G 
*************************** 1. row *************************** 
    Level: Note 
    Code: 1003 
Message: select `stack`.`am1`.`account_id` AS `id` from 
`stack`.`account_machine` `am1` join (select 
`stack`.`account_machine`.`account_id` AS 
`account_id`,max(`stack`.`account_machine`.`date`) AS `date` from 
`stack`.`account_machine` group by 
`stack`.`account_machine`.`account_id`) `am2` where 
((`stack`.`am1`.`account_id` = `am2`.`account_id`) and 
(`stack`.`am1`.`date` = `am2`.`date`) and (`stack`.`am1`.`machine_id` 
<> 0)) order by `stack`.`am1`.`account_id` 
1 row in set (0.00 sec) 

顯然不是沒有索引的高性能查詢,但是對於有限的數據集來說很好。

0

我懷疑有在查詢設計缺陷 - 如果子查詢回來與account_idmachine_id = 0。之後,它不會再看。

使用JOIN...ON時,將僅限條款中的加入信息,而不是過濾信息是好的形式;那在WHERE

看起來這將是更簡單,更快:

SELECT account_id 
    FROM account_machine AS m 
    WHERE machine_id != 0 
     AND date <= 20170215 
     AND EXISTS (
     SELECT * 
      FROM account 
      WHERE id = m.account_id 
       ) 
    ORDER BY date DESC 
    LIMIT 1; 

也許EXISTS()測試是多餘的,可以去掉?

INDEX(date)可能有助於提高性能。

(不,我還沒有發現,爲什麼在兩個服務器可能會有所改變。看看我的版本的作品。)

+0

你的查詢顯然是錯誤的 - 我想返回所有活動帳戶,但你的限制爲1.同樣,你對'machine_id = 0'的猜測也是錯誤的。我嘗試刪除該子句,但仍然只在MariaDB中獲得2行。 – zhao