2014-12-05 3 views
-1

У меня есть огромный стол (миллионы строк), которая выглядит следующим образом (в сущности)избежать присоединения за столом в 100 раз с собой

datatime    tagname  interesting somemore columns 
2014-12-04 20:00:00 grp1_tagA   77  0  0 
2014-12-04 20:00:00 grp1_tagB   88  0  0 
2014-12-04 20:00:00 grp1_tagC   99  0  0 
2014-12-04 20:00:00 grp2_tagA   11  0  0 
2014-12-04 20:00:00 grp2_tagB   22  0  0 
2014-12-04 20:00:00 grp2_tagC   13  0  0 
2014-12-04 21:00:00 grp1_tagA   17  0  0 
2014-12-04 21:00:00 grp1_tagC   28  0  0 
2014-12-04 21:00:00 grp1_tagC   29  0  0 
2014-12-04 21:00:00 grp2_tagA   31  0  0 
2014-12-04 21:00:00 grp2_tagB   62  0  0 
2014-12-04 21:00:00 grp2_tagC   53  0  0 
2014-12-04 22:00:00 grp1_tagA   87  0  0 
2014-12-04 22:00:00 grp1_tagB   48  0  0 
2014-12-04 22:00:00 grp1_tagC   99  0  0 
2014-12-04 22:00:00 grp2_tagA   51  0  0 
2014-12-04 22:00:00 grp2_tagB   42  0  0 
2014-12-04 22:00:00 grp2_tagC   53  0  0 

В реальной таблице, существуют десятки групп, каждая группа ~ 100 тегов, а для каждой группы и тега есть ежеквартальные данные за несколько лет (так что пара десяти тысяч строк за тэг), составляющая в настоящее время около 8 миллионов строк. На более позднем этапе вступят в игру другие таблицы, которые имеют меньший временной интервал и, следовательно, еще больше.

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

Так что я хочу, чтобы произвести «группа 1 дайджест» таблицу, такие как этого

group1_1

упрощенный запрос будет что-то вроде (сбросив дату ограничения на данный момент)

SELECT A.`datatime` as `datatime`, 
A.`interesting` as tagA, B.`interesting` as tagB, C.`interesting` as tagC 
FROM `everything` A, `everything` B, `everything` C 
WHERE 
A.`datatime` = B.`datatime` AND 
A.`datatime` = C.`datatime` AND 
A.`tagname` = "grp1_tagA" AND 
B.`tagname` = "grp1_tagB" AND 
C.`tagname` = "grp1_tagC" 

Это на самом деле немного сложнее, потому что в какой-то момент некоторые теги могут иметь данные, а другие - нет, и я также хочу, чтобы строки с частичными данными. Так что с еще одной строки

enter image description here

что я хочу

group1_2

Возможный запрос с этой целью является

SELECT GLUE.thyme, A.iwant as tagA, B.iwant as tagB, C.iwant as tagC FROM 
(SELECT distinct `datatime` as thyme from `everything`) GLUE left join 
(SELECT `datatime` as thyme, `interesting` as iwant from `everything` where `tagname` = "grp1_tagA") A on GLUE.thyme = A.thyme left join 
(SELECT `datatime` as thyme, `interesting` as iwant from `everything` where `tagname` = "grp1_tagB") B on GLUE.thyme = B.thyme left join 
(SELECT `datatime` as thyme, `interesting` as iwant from `everything` where `tagname` = "grp1_tagC") C on GLUE.thyme = C.thyme 

Проблема: "реальный мир" версия этот запрос не достаточно быстрый. Я протестировал вышеуказанную структуру запроса с 34 именами тегов (делая 35 табличных объединений), добавив ограничение даты, например where/and datatime >= '2013-12-04', к , каждый из подзапросов, так что было возвращено в общей сложности 8760 строк (то есть 1 год данных). Полученное время выполнения составляло 2 с половиной минуты. Я ориентируюсь на что-то гораздо меньше половины минуты, настало время для передачи данных через Интернет.

Большая таблица имеет составной индекс первичного ключа по времени и тэгу и индекс (ключ) по времени.

Общий вопрос: как я могу получить данные быстрее?

Вопрос 1 Можно ли улучшить вышеуказанный запрос?

Это было бы предпочтительным решением.

Обновление Принятый ответ дает предпочтительное решение. Этот запрос можно написать БЕЗ КАКИХ-ЛИБО СОЕДИНЕНИЙ. И это намного быстрее. (До двух секунд от 2,5 минут, просто протестируйте его.) Не нужно читать остальную часть вопроса, поэтому лишняя таблица не нужна.

Если это невозможно, существует возможность сохранения дополнительной таблицы group1, которая имеет все данные результата запроса, для всего доступного диапазона дат и в некотором смысле хранится в синхронизации с большой таблицей , вероятно, срабатывает. Это то, над чем я сейчас работаю, но я подозреваю, что триггер, который у меня есть, не будет работать достаточно быстро.

Так создать новую таблицу,

CREATE TABLE `group1` (
    `datatime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `tagA` int(32) DEFAULT NULL, 
    `tagB` int(32) DEFAULT NULL, 
    `tagC` int(32) DEFAULT NULL 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

Перенос данных из большой таблицы в новую таблицу

INSERT INTO group1 (`datatime`) SELECT DISTINCT `datatime` from `everything`; 

UPDATE group1 g, (SELECT `datatime` as thyme, `interesting` as iwant from `everything` where `tagname` = "grp1_tagA") as source set g.`tagA` = iwant WHERE g.`datatime`= source.thyme; 
UPDATE group1 g, (SELECT `datatime` as thyme, `interesting` as iwant from `everything` where `tagname` = "grp1_tagB") as source set g.`tagB` = iwant WHERE g.`datatime`= source.thyme; 
UPDATE group1 g, (SELECT `datatime` as thyme, `interesting` as iwant from `everything` where `tagname` = "grp1_tagC") as source set g.`tagC` = iwant WHERE g.`datatime`= source.thyme; 

Trigger, чтобы сохранить новую таблицу в синхронизации с большим столом

CREATE TRIGGER everything_group1_after_insert 
AFTER INSERT 
    ON `everything` FOR EACH ROW 
BEGIN 
    DECLARE counter INT; 
    SET counter = (SELECT count(*) FROM `group1` WHERE datatime = NEW.`datatime`); 
    IF counter = 0 THEN 
     INSERT INTO `group1` (`datatime`) VALUES (NEW.`datatime`); 
    END IF; 
    IF NEW.TAGNAME = "grp1_tagA" THEN UPDATE `group1` SET `tagA` = NEW.`interesting` WHERE `group1`.`datatime` = NEW.`datatime`; END IF; 
    IF NEW.TAGNAME = "grp1_tagB" THEN UPDATE `group1` SET `tagB` = NEW.`interesting` WHERE `group1`.`datatime` = NEW.`datatime`; END IF; 
    IF NEW.TAGNAME = "grp1_tagC" THEN UPDATE `group1` SET `tagC` = NEW.`interesting` WHERE `group1`.`datatime` = NEW.`datatime`; END IF; 
END; // 
DELIMITER ; 

Вопрос 2 Как улучшить время выполнения курок? Или поддерживать синхронизацию таблицы по-разному (не обязательно с помощью триггера)? Невозможно ли иметь 1 оператор if для каждого тега?

Вопрос 3 Предположим, в группу были добавлены новые теги. Возможно ли вообще написать триггер (или запрос, см. Вопрос 1) таким образом, что в этом случае его не нужно переписывать для учета новых тегов/столбцов таблицы результатов? Для запроса я уверен, что это невозможно (это повлечет за собой неуказанное количество подключаемых таблиц), но, возможно, это возможно для триггера?

вы можете скачать SQL дамп для вышеупомянутых игрушечной базы данных здесь: toy database

обновления: Я забыл первичный ключ на group1

alter table `group1` add primary key (datatime) 
+1

небольшого куска советов. Я начал читать вопрос и подумал: «О, я знаю, как это исправить». И потом я прочитал немного больше. И прокрутил еще немного. И увидел несколько нумерованных вопросов и сдался. Другими словами, ваш вопрос просто сложный (для меня), чтобы рассмотреть вопрос об ответе на этот форум. –

+0

Моя немедленная реакция заключается в том, что даже если вы решите сохранить свой составный столбец 'tagname', вы должны, по крайней мере, рассмотреть возможность разделения столбцов,' groupname' и 'tagwithingroup'. Это сделает запросы проще писать, почти наверняка. Я бы рассмотрел возможность удаления существующего столбца «тэг» (или заставить его удерживать то, что я назвал «tagwithingroup»); вы можете легко создать его значение, когда вам нужно его представить. Вероятно, вам нужен индекс в столбцах groupname и tagwithingroup, возможно, с столбцом даты/времени, добавленным как третий элемент в индексе (который может быть тогда уникальным индексом). –

+0

@ Джонатан: У меня нет возможности перепроектировать базу данных. Это «промышленные» данные из объекта очистки воды, я там не работаю, я просто визуализирую данные для них. Но да, расщепление столбца тэгов было бы хорошей идеей (для них.) – mathheadinclouds

ответ

3

Попробуйте использовать группу, на datatime колонке, и случай следующим образом.

SELECT a.datatime 
    , sum(case when a.tagname = 'grp1_tagA' then a.interesting else NULL end) as tagA 
    , sum(case when a.tagname = 'grp1_tagB' then a.interesting else NULL end) as tagB 
    , sum(case when a.tagname = 'grp1_tagC' then a.interesting else NULL end) as tagC 
FROM everything AS a 
WHERE a.datatime >= '2013-12-04' 
GROUP BY a.datatime 
; 
+0

Вот и все! Намного быстрее! Я просто заменил ноль на NULL, вот что я собираюсь использовать в производстве. – mathheadinclouds

+0

Рад, что это сработало для вас.Я обновил ответ, чтобы использовать NULL вместо нуля в операторах case. – BateTech

+0

Вы можете сконденсировать SQL с помощью 'IF', а не' CASE': 'SUM (IF (a.tagname = 'grp1_tagA, a.interesting, NULL)) как TagA'. http://en.wikibooks.org/wiki/MySQL/Pivot_table –

0

Испытание на огромном столе с миллионами строк, показало, что отличный ответ BateTech по-прежнему может быть немного улучшились, например, так

SELECT a.datatime 
    , sum(case when a.tagname = 'grp1_tagA' then a.interesting else NULL end) as tagA 
    , sum(case when a.tagname = 'grp1_tagB' then a.interesting else NULL end) as tagB 
    , sum(case when a.tagname = 'grp1_tagC' then a.interesting else NULL end) as tagC 
FROM (SELECT * FROM everything WHERE datatime >= '2013-12-04' and tagname like "grp1_%") AS a 
GROUP BY a.datatime 
; 
Смежные вопросы