2017-02-17 25 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, но Безразлично» t с помощью 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- me-to-be-a-very-simple-sql-query – 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) 

Я бы любопытно увидеть результаты EXPLAIN EXTENDED/SHOW ПРЕДУПРЕЖДЕНИЙ из обоих MySQL и MariaDB. Это покажет вам, как оптимизатор запросов переписывает запрос. Например:

[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_id для machine_id = 0. После этого он не будет выглядеть дальше.

При использовании JOIN...ON это хорошая форма поставить только на соединяющую информацию в пункте 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' также неверна. Я попытался удалить это предложение, но у меня осталось только 2 строки в MariaDB. – zhao

 Смежные вопросы

  • Нет связанных вопросов^_^