Обходной путь для MYSQL Full GROUP BY

Я работаю над запросом, который должен быть максимально универсальным для повторного использования. Запрос включает GROUP BY.

Однако я не могу указать столбцы, так как мне нужно, чтобы они были универсальными.

Цель запроса:

  • Назначает ключ времени каждой строке, используя столбец даты.
  • Затем я использую GROUP BY, чтобы получить только несколько значений группы.
  • В основном то, что я хочу сделать, это уменьшить количество очков в данный день.
  • Итак, если есть, скажем, 4 точки в день, я использую GROUP BY, чтобы уменьшить их до 2 точек в день, а затем выбрать то же самое, скажем, в новую базу данных.

Запрос

SELECT * FROM
   (SELECT h.* FROM testdb h
   WHERE h.date <= '2016-01-02 23:30:00'
   GROUP BY FLOOR(UNIX_TIMESTAMP(h.date)/((1440/2)*60))
   UNION
   SELECT c.* FROM testdb c
   WHERE c.date> '2016-01-02 23:30:00') m;

Я могу запустить вышеуказанный запрос в MySQL v5.7 и выше, отключив режим ONLY_FULL_GROUP_BY. Когда я это делаю, результат запроса меняется.

Результат MySQL ‹5.7: 17 строк.

MySQL > 5.7 (с отключенной полной группой) результат 18 строк.

Мои сомнения:

  • Почему набор результатов меняется, группа не должна фактически изменять результат в моем случае.
  • Любая работа, чтобы добиться того же, не отключая FULL GROUP BY?

Читал про MySQL ANY_VALUE, но опять же не работает с SELECT ANY_VALUE(*)

Нужна помощь в том, как добиться вышеперечисленного :) Спасибо

Обновление 1

Локальный компьютер:

Версия SQL 5.7.24

Режим SQL:

STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER, NO_ENGINE_SUBSTITUTION

Спорный запрос:

   SELECT * FROM
   (SELECT h.* FROM testdb h
   WHERE h.date <= '2016-01-02 23:30:00'
   GROUP BY FLOOR(UNIX_TIMESTAMP(h.date)/((1440/2)*60))
   UNION
   SELECT c.* FROM testdb c
   WHERE c.date> '2016-01-02 23:30:00') m;

Действия для воспроизведения проблемы:

CREATE TABLE testdb ( id int primary key auto_increment,date timestamp);


    INSERT INTO testdb (date) VALUES ('2015-12-31 00:00:00');
    INSERT INTO testdb (date) VALUES ('2015-12-31 06:00:00'); 
    INSERT INTO testdb (date) VALUES ('2015-12-31 18:00:00');
    INSERT INTO testdb (date) VALUES ('2016-01-01 00:00:00');
    INSERT INTO testdb (date) VALUES ('2016-01-01 06:00:00');
    INSERT INTO testdb (date) VALUES ('2016-01-01 18:00:00');
    INSERT INTO testdb (date) VALUES ('2016-01-02 00:00:00'); 
    INSERT INTO testdb (date) VALUES ('2016-01-02 06:00:00'); 
    INSERT INTO testdb (date) VALUES ('2016-01-02 18:00:00'); 
    INSERT INTO testdb (date) VALUES ('2016-12-31 00:00:00'); 
    INSERT INTO testdb (date) VALUES ('2016-12-31 06:00:00'); 
    INSERT INTO testdb (date) VALUES ('2016-12-31 06:01:00'); 
    INSERT INTO testdb (date) VALUES ('2016-12-31 12:00:00'); 
    INSERT INTO testdb (date) VALUES ('2016-12-31 18:00:00'); 
    INSERT INTO testdb (date) VALUES ('2017-01-01 00:00:00'); 
    INSERT INTO testdb (date) VALUES ('2017-01-01 06:00:00');
    INSERT INTO testdb (date) VALUES ('2017-01-01 18:00:00');
    INSERT INTO testdb (date) VALUES ('2017-01-02 00:00:01'); 
    INSERT INTO testdb (date) VALUES ('2017-01-02 06:00:00'); 
    INSERT INTO testdb (date) VALUES ('2017-01-02 18:00:00');

Результат запроса:

18 рядов

Скрипка БД

https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=6082783c8a2c8ab1aa9bb5846

Версия SQL 5.7.26

РЕЖИМ SQL

STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER, NO_ENGINE_SUBSTITUTION

Результат запроса:

17 рядов


person Abhilash Gopalakrishna    schedule 28.06.2019    source источник
comment
Пожалуйста, включите запрос в вопрос как text. Кроме того, объясните, что должен делать запрос.   -  person Gordon Linoff    schedule 28.06.2019
comment
Поведение группы bx немного изменилось, вы можете сравнить битовую версию, чтобы увидеть, как dev.mysql.com/doc/refman/5.7/en/group-by-handling.html и dev.mysql.com/doc/refman/5.6/en/group-by-handling.html   -  person nbk    schedule 28.06.2019
comment
@GordonLinoff Я обновил вопрос, указав его цель. Приведенный ниже запрос не работает для меня, так как мне нужно иметь возможность выбирать все столбцы в данной таблице, а не только количество. Извините за двусмысленность :) Я так понимаю это крайний случай, может такое быть?? :)   -  person Abhilash Gopalakrishna    schedule 28.06.2019
comment
@AbhilashGopalakrishna Итак, согласно вашему обновленному вопросу, вам не требуется подсчет записей, но вы хотите, чтобы фактические записи были сокращены до 2 записей в день ??   -  person Prashanth    schedule 28.06.2019
comment
Одинаков ли часовой пояс на каждом компьютере?   -  person Rick James    schedule 28.06.2019
comment
@RickJames Привет, да, работает на локальной виртуальной машине. Однако я не думаю, что это будет проблемой, так как я запускал один и тот же запрос, используя докер, для разных версий MySQL. Также я просто сравниваю формат даты, доступный в самой таблице, с заданной датой.   -  person Abhilash Gopalakrishna    schedule 28.06.2019
comment
Хм... Скрипка работает для меня. Можете ли вы настроить другой Fiddle, который не работает в 5.7?   -  person Rick James    schedule 28.06.2019
comment
Просто поместите это в начало выбора: SET sql_mode = ''; -- я получаю 17 строк. Между тем, что вы получаете с предложением Гордона?   -  person Rick James    schedule 28.06.2019
comment
@RickJames dbfiddle.uk/ это странно, скрипка работает отлично. но то же самое не работает в докере и моей локальной среде. Возможно, проблема связана с каким-то другим режимом. В недоумении :) пробую еще раз в докере и локально теперь со свежей установкой. Если это сработает, я закрою этот вопрос :)   -  person Abhilash Gopalakrishna    schedule 28.06.2019
comment
Предложение @RickJames Gordon сработало для подсчета, но без отключения полной группы нет другого выхода, ищем способ оптимизировать сам запрос, а не устанавливать полную группу :)   -  person Abhilash Gopalakrishna    schedule 28.06.2019
comment
@AbhilashGopalakrishna - Вместо sql_mode='' или SELECT h.* или SELECT ANY_VALUE(h.id), ... вы можете сделать SELECT MIN(h.id), ....   -  person Rick James    schedule 28.06.2019
comment
@RickJames Привет, я сделал новую установку mysql и попробовал то же самое. Я все еще получаю 18 строк вопреки ошибке dbfiddle, то же самое и в докере. Я обновлю вопрос, указав подробности моего сервера mysql. Я озадачен тем, почему я получаю ряд больше в моей машине.   -  person Abhilash Gopalakrishna    schedule 28.06.2019
comment
Между тем, внешний SELECT * FROM (...) можно удалить.   -  person Rick James    schedule 28.06.2019
comment
Покажите нам результаты 17 и 18.   -  person Rick James    schedule 03.07.2019


Ответы (2)


Это, казалось бы, делает то, что вы хотите:

SELECT COUNT(*)
FROM (SELECT FLOOR(UNIX_TIMESTAMP(h.date)/((1440/2)*60)) FROM testdb h
      WHERE h.date <= '2016-01-02 23:30:00'
      GROUP BY FLOOR(UNIX_TIMESTAMP(h.date)/((1440/2)*60))
      UNION
      SELECT c.date
      FROM testdb c
      WHERE c.date> '2016-01-02 23:30:00'
     ) m;
person Gordon Linoff    schedule 28.06.2019
comment
Можно также сделать его UNION ALL, так как диапазоны не перекрываются. 5.7 (и 10.1), чтобы избежать создания временной таблицы. -- bugs.mysql.com/bug.php?id=50674 - person Rick James; 28.06.2019
comment
Привет, мне нужны все столбцы, но что мне более характерно, так это вариации в наборе результатов запроса. Обновили всю информацию. где может быть проблема? - person Abhilash Gopalakrishna; 28.06.2019

Перенося Гордоны Ответьте дальше:

SELECT  ( SELECT COUNT(DISTINCT FLOOR(UNIX_TIMESTAMP(date)/((1440/2)*60)))
                          FROM testdb WHERE date <= '2016-01-02 23:30:00' )
      + ( SELECT COUNT(*) FROM testdb WHERE date >  '2016-01-02 23:30:00');

То есть не беспокойтесь о получении каких-либо значений, поскольку вам, похоже, нужен только счет.

(Если вам нужны значения, то какие id и т. д. вам нужны??)

Эта формулировка будет работать быстрее, чем любая другая, особенно если у вас есть INDEX(date).

person Rick James    schedule 28.06.2019
comment
Привет, мне нужны все столбцы, а не только количество, обновил вопрос. Более того, я не могу понять, как результаты меняются на 1 строку. Режимы SQL тоже одинаковые, я их менял и пробовал запрос. Но все равно моя машинка выдает 18 рядов. В чем может быть проблема? - person Abhilash Gopalakrishna; 28.06.2019
comment
@ Я пишу этот запрос так, чтобы он работал с любой таблицей со столбцом date - timestamp, поэтому с использованием select * - person Abhilash Gopalakrishna; 28.06.2019
comment
@AbhilashGopalakrishna - Тогда какой id вы хотите, когда несколько сгруппированы вместе?? - person Rick James; 28.06.2019
comment
Я хочу получить любые существующие столбцы. Я использую select * с group by timekey — новым столбцом, который я ввожу для получения требуемого результата. Это гарантирует, что, не зная, какие все столбцы есть в таблице, я могу применить группу, используя новый столбец - timekey и выбрать любой другой существующий столбец, используя select * - person Abhilash Gopalakrishna; 03.07.2019