Как мне сделать сложную GROUP BY в MySQL?

8

У меня есть таблица, которая содержит несколько ключей в другие таблицы (где каждый ключ состоит из нескольких столбцов). Я хотел бы иметь возможность группировать строки, имеющие одинаковый ключ, но я не хочу группировать их все вместе. Это не просто GROUP BYпо ключу, а скорее я хочу, чтобы можно было создать группы, скажем, 10. Так что, если конкретный ключ появлялся 50 раз, я получал бы 5 результатов при выполнении этой группировки (5 групп по 10). Я также хочу, чтобы эта группировка происходила случайным образом в ключе.

Я не знал о прямом способе сделать это, и метод обхода, который я придумал, не работает, как я думаю, что должен. Обходным решением, которое я придумал, было создание нового столбца для каждого ключа, который был бы целым числом, таким образом, что значение iпредставляет ithвхождение этого ключа (но в случайном порядке). Затем я мог бы сделать целочисленное деление, чтобы все n (скажем, 10) строк в ключе имели одинаковое значение, и я мог бы сделать для GROUP BYэтого значения.

Есть ли более прямой способ выполнить то, что я только что описал? Это довольно неловко, и я столкнулся с проблемами при создании нового столбца индекса (как я описал в этом вопросе ).

РЕДАКТИРОВАТЬ: Прежде всего, обратите внимание, что это для MySQL. Я добавлю пример, если моя цель не ясна. Документы MySQL показывают метод, чтобы добраться почти до цели :

CREATE TABLE animals (
    grp ENUM('fish','mammal','bird') NOT NULL,
    id MEDIUMINT NOT NULL AUTO_INCREMENT,
    name CHAR(30) NOT NULL,
    PRIMARY KEY (grp,id)
) ENGINE=MyISAM;

INSERT INTO animals (grp,name) VALUES
    ('mammal','dog'),('mammal','cat'),
    ('bird','penguin'),('fish','lax'),('mammal','whale'),
    ('bird','ostrich');

SELECT * FROM animals ORDER BY grp,id;

Это создает таблицу, которая, хотя и не то, что я хочу, приближается:

+--------+----+---------+
| grp    | id | name    |
+--------+----+---------+
| fish   |  1 | lax     |
| mammal |  1 | dog     |
| mammal |  2 | cat     |
| mammal |  3 | whale   |
| bird   |  1 | penguin |
| bird   |  2 | ostrich |
+--------+----+---------+

По сути, я хотел бы GROUP BYидентифицировать, за исключением того, что я хотел бы, чтобы записи mammalимели одну «группу» для идентификаторов 1–10, другую «группу» для идентификаторов 11–20 и т. Д. Однако я бы делал это с существующей таблицей, и я бы не хотел, чтобы «собака» появлялась с идентификатором 1. Я бы хотел, чтобы этот первоначальный порядок был случайным, но затем детерминированным.

Майкл МакГоуэн
источник
I would want that initial ordering to be random, but then deterministic from then out.<- скажи что? Я думаю, что независимо от того, что вы делаете, вы должны будете поместить записи во вторую таблицу. Насколько точно работает эта бизнес-логика? Так как нет ничего, что требовало бы (например) собаки, чтобы прийти первым. И что вы подразумеваете под I would want the records from *mammal* to have one "group" for IDs 1-10, and another for IDs 11-20... можете ли вы проиллюстрировать это другой таблицей, посвященной млекопитающим, в приведенном выше описании вопроса?
Jcolebrand
@jcolebrand Для каждой записи млекопитающего я хочу назначить уникальный идентификатор от 1 до numMammal. Меня не волнует, что dogполучает id , но я не хочу, чтобы он зависел от исходного порядка вставки.
Майкл МакГоуэн
@jcolebrand Предположим, у меня также есть столбец веса. Я мог бы хотеть взять средний вес млекопитающих с идентификаторами от 1 до 10 и средний вес млекопитающих с идентификаторами от 11 до 20 и т. Д. Это то чувство, которое я хочу GROUP BY. Я мог бы тогда хотеть соединить группы из 10, чтобы найти корреляцию между средним. Мне нужен этот случайный порядок, потому что если бы исходный порядок вставки был отсортирован по весу, это дало бы мне неверные результаты. Я надеюсь, что я понимаю.
Майкл МакГоуэн
Я все еще думаю, что образец ТАБЛИЦЫ в вопросе будет полезен. Но я думаю, что вижу то, что вы хотите. Я просто не понимаю, где эти вещи относятся к области SQL, поскольку речь не идет о множествах. SQL - это область множеств. Я бы сделал логику, которую вы предлагаете, в php-файле с одним (или двумя) циклами. SQL будет делать эффективный единственный цикл для присвоения номеров в любом случае.
Jcolebrand
@jcolebrand Вполне может быть, что я не должен был делать это в SQL, но я подумал, что полезное правило заключается в том, чтобы позволить базе данных делать всю работу за вас. Я все еще изучаю границы того, что следует и не нужно обрабатывать в базе данных, но в прошлом, когда я пытался извлекать результаты, обрабатывать их, а затем вставлять результаты обратно, я получал плохие результаты производительности (часы и часы, потому что я, вероятно, делал что-то не так, вставляя результаты обратно).
Майкл МакГоуэн

Ответы:

5

Как насчет того, чтобы сделать небольшую математику с вашим столбцом идентификаторов для динамического создания группы?

SELECT grp, FLOOR(id/10) AS id_grp
FROM animals
GROUP BY grp, id_grp

Это даст вам группы по 10 на основе идентификатора записи. Я использовал вашу таблицу животных выше, чтобы сгенерировать данные ниже.

Образец данных

 INSERT INTO animals VALUES
 ('mammal',10,'dog'),('mammal',11,'dog'),('mammal',12,'dog'),
 ('mammal',21,'cat'),('mammal',22,'cat'),('mammal',23,'cat'),
 ('mammal',24,'cat'),('mammal',25,'cat'),('mammal',26,'cat'),
 ('bird',30,'penguin'),('bird',31,'penguin'),('bird',32,'penguin'),
 ('bird',33,'penguin'),('fish',44,'lax'),('fish',45,'lax'),
 ('fish',46,'lax'),('fish',47,'lax'),('fish',48,'lax'),
 ('mammal',31,'whale'),*'fish',51,'lax'),('fish',52,'lax'),
 ('fish',53,'lax'),('fish',54,'lax'),('bird',10,'ostrich');

Вывод запроса

 +--------+--------+
 | grp    | id_grp |
 +--------+--------+
 | fish   |      4 |
 | fish   |      5 |
 | mammal |      1 |
 | mammal |      2 |
 | mammal |      3 |
 | bird   |      1 |
 | bird   |      3 |
 +--------+--------+
 7 rows in set (0.00 sec)
nabrond
источник
Я планировал сделать аналогичную математику, если бы я мог сначала создать соответствующую таблицу. У меня проблемы с назначением идентификаторов.
Майкл МакГоуэн
Помогает ли это вообще @MichaelMcGowan? объяснение xtended.com/2009/03/05/row-sampling
jcolebrand
@jcolebrand Спасибо, я все еще смотрю на первую ссылку. Я попробовал подход, аналогичный 2-й ссылке, и у меня
Майкл МакГоуэн
2

В SQL, как правило, это будет:

  • ОТЛИЧИТЕЛЬНЫЙ отбор
  • Вернуться к главной таблице на клавишах DISTINCT
  • NTILE с PARTITION BY на ключах DISTINCT и ORDER BY для создания сегментов

Это не агрегат, поэтому GROUP BY не требуется

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

На самом деле, NTILE достаточно, чтобы создать «n блоков на набор различных значений»

ГБН
источник
Я не верю, что MySQL поддерживает NTILE.
Майкл МакГоуэн
Извините, эта ссылка подразумевает, что это так. Вероятно, есть решение / обходной путь для NTILE.
ГБН
Отличное решение Oracle.
Ли Риффель
@Leigh Riffel: и SQL Server. И Sybase. И PostGres ...
gbn
2
@gbn Не MySQL был моментом, который я должен был прояснить. В статье упоминается Oracle.
Ли Риффель
1

Я до сих пор не вижу полных решений (которые действительно работают в MySQL), так что это решение, которое я, вероятно, буду использовать:

  1. Генерация случайных идентификаторов вне SQL полностью (в каком-то сценарии)
  2. Примените целочисленное деление к этим идентификаторам, чтобы сгруппировать их соответственно.

Я все еще надеюсь, что кто-то сможет победить этот ответ; Я не хочу принимать мой собственный ответ. Я говорил это раньше, но с самого начала я знал, как это сделать # 2; # 1 это то, что беспокоило меня. Если вы можете ответить на # 1, тогда вы на самом деле ответите и на другой вопрос , но, возможно, можно будет ответить на этот вопрос иным способом, чтобы обойти # 1.

Майкл МакГоуэн
источник
0
-- Change 'ValueField' to whatever provides your 'group' values

set @rownum := 0;
set @groupnum := 0;
set @lastGroup := 0;

select
    ValueField, 
    Grouping, 
    count(1) as Count
from
    (
        -- We have a row number for each record
        select
            -- Set the record number
            case when @lastGroup != ValueField 
                then @rownum := 0 else (@rownum := @rownum + 1) 
            end as Record, 

            -- Determine which group we are in
            case
                -- If the 'Group' changed, reset our grouping
                when @lastGroup != ValueField 
                    then @groupnum := 0

                -- Determines the grouping value; group size is set to 10
                when floor(@rownum / 10) != @groupnum 
                    then @groupnum := @groupnum + 1 
                else @groupnum
            end as Grouping,

            -- Track the last Group
            case 
                when @lastGroup != ValueField 
                    then @lastGroup := ValueField 
                else @lastGroup 
            end as LastGroup,

            -- Value field that will be aggregated
            ValueField 
        from 
            YourTable
        order by 
            ValueField
    ) as x
group by
    ValueField, 
    Grouping;
dba4life
источник