Как группировать, группировать и фильтровать наборы результатов для очень большого набора данных - PullRequest
3 голосов
/ 06 августа 2010

Извините, название вопроса несколько расплывчато, так что вот рабочий пример.

У меня есть таблица, в которую каждый пользователь (идентификатор пользователя) получает значение каждые несколько дней. Я хочу найти последнее из этих значений для каждого пользователя с разбивкой по месяцам и подсчитать их число в диапазоне.

Вот пример таблицы и репрезентативные данные:

CREATE TABLE `datasource` (
    `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
    `userId` INT UNSIGNED NOT NULL ,
    `unixts` INT UNSIGNED NOT NULL ,
    `value` INT UNSIGNED NOT NULL ,
    INDEX ( `userId` )
);

INSERT INTO `datasource` 
    (`userId`, `unixts`, `value`)
VALUES 
    (1, UNIX_TIMESTAMP('2010-07-01'), 500),
    (1, UNIX_TIMESTAMP('2010-07-15'), 610),
    (1, UNIX_TIMESTAMP('2010-08-02'), 740),

    (2, UNIX_TIMESTAMP('2010-07-03'), 506),
    (2, UNIX_TIMESTAMP('2010-07-18'), 640),
    (2, UNIX_TIMESTAMP('2010-08-09'), 340),

    (3, UNIX_TIMESTAMP('2010-07-03'), 506),
    (3, UNIX_TIMESTAMP('2010-08-18'), 640)
;

Теперь вот запрос, чтобы получить то, что я после:

select
    month(FROM_UNIXTIME(unixts)) as month,
    sum( if( value >= 700, 1, 0) ) as '700 and up',
    sum( if( value BETWEEN 600 AND 699, 1, 0) ) as '600-699',
    sum( if( value BETWEEN 500 AND 599, 1, 0) ) as '500-599',
    sum( if( value <= 499, 1, 0) ) as '499 and below',
    count(*) as total
from
    datasource
where
    id in (
        select 
            max(id)
        from 
            datasource 
        where 
            unixts between UNIX_TIMESTAMP('2010-07-01') and UNIX_TIMESTAMP('2010-09-01')
        group by 
            userId, month(from_unixtime(unixts))
    )
group by
    month(FROM_UNIXTIME(unixts));

+-------+------------+---------+---------+---------------+-------+
| month | 700 and up | 600-699 | 500-599 | 499 and below | total |
+-------+------------+---------+---------+---------------+-------+
|     7 |          0 |       2 |       1 |             0 |     3 |
|     8 |          1 |       1 |       0 |             1 |     3 |
+-------+------------+---------+---------+---------------+-------+

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

Есть ли оптимизированный способ написать этот запрос, который может достичь того, что я хочу, не привязывая mysql к нескольким дням?

Ответы [ 2 ]

2 голосов
/ 06 августа 2010

Попробуйте EXPLAIN select ...;

Это скажет вам, как работает запрос. Затем вы можете увидеть, происходит ли полное сканирование таблицы по какой-либо причине, и принять меры для ее исправления. Это, вероятно, будет включать в себя предложение Cfreak. Кроме того, опубликуйте результаты здесь, и мы увидим, что мы можем сделать.

0 голосов
/ 06 августа 2010

Создать индекс для столбца значений.

create index value_index ON datasource(value)

Вы должны сделать это только один раз. Это немного замедлит ваши вставки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...