2017-02-18 34 views
3

Мне много раз говорили, что те же запросы MariaDB будут работать так же, как и на MySQL ... пока я не встречу эту проблему.«GROUP BY» на MariaDB ведет себя иначе, чем MySQL

В последнее время я пытаюсь клонировать приложение из MySQL (InnoDB) для MariaDB (XtraDB). Хотя MariaDB запускает MySQL-запросы без необходимости изменения чего-либо, я был удивлен, обнаружив, что одни и те же запросы действительно ведут себя по-разному на обеих платформах, особенно в ORDER BY и GROUP BY.

Для примера:

MyTable 
    ======= 
    +----+----------+---------------------+-----------+ 
    | id | parentId | creationDate  | name  | 
    +----+----------+---------------------+-----------+ 
    | 1 | 2357  | 2017-01-01 06:03:40 | Anna  | 
    +----+----------+---------------------+-----------+ 
    | 2 | 5480  | 2017-01-02 07:13:20 | Becky  | 
    +----+----------+---------------------+-----------+ 
    | 3 | 2357  | 2017-01-03 08:20:12 | Christina | 
    +----+----------+---------------------+-----------+ 
    | 4 | 2357  | 2017-01-03 08:20:15 | Dorothy | 
    +----+----------+---------------------+-----------+ 
    | 5 | 5480  | 2017-01-04 09:25:45 | Emma  | 
    +----+----------+---------------------+-----------+ 
    | 6 | 1168  | 2017-01-05 10:30:10 | Fiona  | 
    +----+----------+---------------------+-----------+ 
    | 7 | 5480  | 2017-01-05 10:33:23 | Gigi  | 
    +----+----------+---------------------+-----------+ 
    | 8 | 1168  | 2017-01-06 12:46:34 | Heidi  | 
    +----+----------+---------------------+-----------+ 
    | 9 | 1168  | 2017-01-06 12:46:34 | Irene  | 
    +----+----------+---------------------+-----------+ 
    | 10 | 2357  | 2017-01-07 14:58:37 | Jane  | 
    +----+----------+---------------------+-----------+ 
    | 11 | 2357  | 2017-01-07 14:58:37 | Katy  | 
    +----+----------+---------------------+-----------+ 

В основном то, что я хочу, чтобы получить из запроса является последними записями из каждой группы (т.е. parentId). К последним, я имею в виду MAX (creationDate) и MAX (id)

Таким образом, для приведенного выше примера, поскольку существует только три различных значения ParentID, я надеюсь получить:

+----+----------+---------------------+-----------+ 
    | id | parentId | creationDate  | name  | 
    +----+----------+---------------------+-----------+ 
    | 11 | 2357  | 2017-01-07 14:58:37 | Katy  | 
    +----+----------+---------------------+-----------+ 
    | 9 | 1168  | 2017-01-06 12:46:34 | Irene  | 
    +----+----------+---------------------+-----------+ 
    | 7 | 5480  | 2017-01-05 10:33:23 | Gigi  | 
    +----+----------+---------------------+-----------+ 

Первоначально приложение есть вопросы, подобные этим способом:

SELECT * FROM 
    (SELECT * FROM `MyTable` WHERE `parentId` IN (...) 
    ORDER BY `creationDate` DESC, `id` DESC) AS `t` 
    GROUP BY `parentId`; 

на MySQL, это работает, так как внутренний запрос будет заказать, а затем внешний запрос получает первый из каждой группы из RESU lt внутреннего запроса. Внешний запрос в основном подчиняется упорядочению внутреннего запроса.

Но на MariaDB внешний запрос будет игнорировать упорядочение результата внутреннего запроса. Я получаю это на MariaDB вместо:

+----+----------+---------------------+-----------+ 
    | id | parentId | creationDate  | name  | 
    +----+----------+---------------------+-----------+ 
    | 1 | 2357  | 2017-01-01 06:03:40 | Anna  | 
    +----+----------+---------------------+-----------+ 
    | 2 | 5480  | 2017-01-02 07:13:20 | Becky  | 
    +----+----------+---------------------+-----------+ 
    | 6 | 1168  | 2017-01-05 10:30:10 | Fiona  | 
    +----+----------+---------------------+-----------+ 

Для достижения такого же поведения на MariaDB, я придумал что-то вроде этого. (Не уверен, если это точно, хотя.)

SELECT `t1`.* FROM `MyTable` `t1` LEFT JOIN `MyTable` `t2` ON (
     `t1`.`parentId` = `t2`.`parentId` 
    AND `t2`.`parentId` IN (...) 
    AND `t1`.`creationDate` <= `t2`.`creationDate` 
    AND `t1`.`id` < `t2`.`id`) 
) WHERE `t2`.`id` IS NULL; 

Теперь проблема ... Если я собираюсь переписать запросы, я должен переписать их сотни ... и они, как некоторые немного бит друг от друга.

Интересно, есть ли у кого-нибудь идеи, которые позволили бы мне сделать наименьшие изменения.

Благодарим вас заблаговременно.

+0

Руководство MySQL охватывает это. И он предлагает 3 действительных решения. Если вы все еще боретесь, см. 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

+0

проверить для вас версию mysql и mariaDB и проверить, как должен использоваться столбец в элементе select by group by clause .. может возникнуть проблема, связанная с only_full_group_by режим установки другого поведения в вашей версии db .. вы может видеть некоторую информацию https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html – scaisEdge

+0

MySQL версия 5.5.52 ... Версия MariaDB - 10.1.21 ... У меня есть попытался запустить SET sql_mode = "ONLY_FULL_GROUP_BY" на MariaDB, но нет никакой разницы. – user2526586

ответ

1

Ваш первый запрос, вероятно, работать в MySQL, но его поведение не документировано: вы группирование по groupid, но вы выбираете неагрегированные столбцы с * и значение любого из этих необобщенных столбцов неопределенными - если значение, которое вы получаете, является первым значением, которое встречается, это просто «дело удачи».

Это правда, что, даже если это не может считаться правильным, в MySQL я никогда не видел, чтобы этот «трюк» терпел неудачу (и здесь, в stackoverflow, есть много рекомендуемых ответов, предлагающих использовать этот трюк), но MariaDB использует другой механизм оптимизации, и вы не можете полагаться на недокументированное поведение MySQL.

Ваш второй запрос нужно немного корректировки:

and (
    `t1`.`creationDate` < `t2`.`creationDate` 
    or (
    `t1`.`creationDate` = `t2`.`creationDate` 
    and `t1`.`id` < `t2`.`id` 
) 
) 

, потому что сначала вы заказываете по дате создания, а затем, если более чем одна запись одни и те же даты создания вы получаете один с наибольшим идентификатором.

Существуют и другие способы написания одного и того же запроса, например.

select * from mytable 
where id in (
    select max(m.id) 
    from mytable m inner join (
    select parentID, max(creationDate) as max_cd 
    from mytable 
    group by ParentID 
) t on m.parentID = t.parentID and m.creationDate = t.max_cd 
    group by m.parentID, m.creationDate 
) 

, но каждый запрос должен быть переписан отдельно.

Редактировать

Ваш пример является немного более сложным, потому что вы заказываете обоими CreationDate и ид. Позвольте мне объяснить лучше. Первое, что нужно сделать, для каждого ParentID вы должны получить последнюю CreationDate:

select parentID, max(creationDate) as max_cd 
from MyTable 
group by parentID 

то для каждого максимального CreationDate вы должны получить высокий ID:

select t.parentID, t.max_cd, max(t.id) as max_id 
from 
    MyTable t inner join ( 
    select parentID, max(creationDate) as max_cd 
    from MyTable 
    group by parentID 
) t1 on t.parentID = t1.parentID and t.creationDate = t1.max_cd 
group t.parentID, t.max_cd 

, то вы должны получить все записи где id возвращается этим запросом. В этом конкретном контексте ЛЕВЫЙ ПРИСОЕДИНЕНИЕ с самой таблицей должно быть проще в написании и более результативном.

+0

Я не знаю, полностью ли я вас понимаю ... так что, если я буду переписывать свой пример SQL с помощью вашего предложения, будет ли это правильно? SELECT * FROM 'MyTable' WHERE' id' IN ( \t SELECT MAX ('id') FROM' MyTable' WHERE 'parentId' IN (...) GROUP BY' creationDate' HAVING 'creationDate' = MAX (' createDate ') ) GROUP BY' parentId'; – user2526586

+0

Спасибо. Это полезно. Теперь мне просто нужно вникать в беспорядок SQL и исправлять по одному ... – user2526586

2

Да, это ответ только для ссылок. Но ссылки на сайт MariaDB.

Вот еще обсуждение «несовместимости»: https://mariadb.com/kb/en/mariadb/group-by-trick-has-been-optimized-away/

Технически говоря, MySQL реализован расширение к стандарту ANSI. Много позже, он решил удалить его, поэтому я думаю, вы обнаружите, что MySQL переместился в MariaDB.

Вот список «быстрых» способов сделать групповой мудр макс, который, вероятно, что вы пытаетесь сделать: https://mariadb.com/kb/en/mariadb/groupwise-max-in-mariadb/