У меня есть огромный стол (миллионы строк), которая выглядит следующим образом (в сущности)избежать присоединения за столом в 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 дайджест» таблицу, такие как этого
упрощенный запрос будет что-то вроде (сбросив дату ограничения на данный момент)
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"
Это на самом деле немного сложнее, потому что в какой-то момент некоторые теги могут иметь данные, а другие - нет, и я также хочу, чтобы строки с частичными данными. Так что с еще одной строки
что я хочу
Возможный запрос с этой целью является
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)
небольшого куска советов. Я начал читать вопрос и подумал: «О, я знаю, как это исправить». И потом я прочитал немного больше. И прокрутил еще немного. И увидел несколько нумерованных вопросов и сдался. Другими словами, ваш вопрос просто сложный (для меня), чтобы рассмотреть вопрос об ответе на этот форум. –
Моя немедленная реакция заключается в том, что даже если вы решите сохранить свой составный столбец 'tagname', вы должны, по крайней мере, рассмотреть возможность разделения столбцов,' groupname' и 'tagwithingroup'. Это сделает запросы проще писать, почти наверняка. Я бы рассмотрел возможность удаления существующего столбца «тэг» (или заставить его удерживать то, что я назвал «tagwithingroup»); вы можете легко создать его значение, когда вам нужно его представить. Вероятно, вам нужен индекс в столбцах groupname и tagwithingroup, возможно, с столбцом даты/времени, добавленным как третий элемент в индексе (который может быть тогда уникальным индексом). –
@ Джонатан: У меня нет возможности перепроектировать базу данных. Это «промышленные» данные из объекта очистки воды, я там не работаю, я просто визуализирую данные для них. Но да, расщепление столбца тэгов было бы хорошей идеей (для них.) – mathheadinclouds