Заобиколно решение за 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 реда

DB Fiddle

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

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
Моля, включете запитването във въпроса като текст. Освен това обяснете какво трябва да прави заявката.   -  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
Хммм... The Fiddle работи за мен. Можете ли да настроите друг 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