Рассмотрим структуру, в которой у вас есть много-к-одному (или один ко многим) отношениям с условием (где, порядок и т. Д.) На обеих таблицах. Например:Возможно ли перекрестное табличное индексирование?
CREATE TABLE tableTwo (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
eventTime DATETIME NOT NULL,
INDEX (eventTime)
) ENGINE=InnoDB;
CREATE TABLE tableOne (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
tableTwoId INT UNSIGNED NOT NULL,
objectId INT UNSIGNED NOT NULL,
INDEX (objectID),
FOREIGN KEY (tableTwoId) REFERENCES tableTwo (id)
) ENGINE=InnoDB;
и для примера запроса:
select * from tableOne t1
inner join tableTwo t2 on t1.tableTwoId = t2.id
where objectId = '..'
order by eventTime;
Допустим, вы индексировать tableOne.objectId
и tableTwo.eventTime
. Если вы затем объясните по вышеуказанному запросу, он покажет «Using filesort». По существу, он сначала применяет индекс tableOne.objectId
, но он не может использовать индекс tableTwo.eventTime
, потому что этот индекс предназначен для всего tableTwo (а не ограниченного набора результатов), и поэтому он должен выполнять ручную сортировку.
Таким образом, есть способ сделать индекс кросс-таблицы, поэтому он не будет иметь значение filesort каждый раз, когда будут получены результаты? Что-то вроде:
create index ind_t1oi_t2et on tableOne t1
inner join tableTwo t2 on t1.tableTwoId = t2.id
(t1.objectId, t2.eventTime);
Кроме того, я посмотрел на создание представления и индексации, но индексация не поддерживается для представлений.
Решение, к которому я склонялся, если индексирование перекрестных таблиц невозможно, это копирование условных данных в одну таблицу. В этом случае значение eventTime
будет реплицировано в tableOne
, а индекс с несколькими столбцами будет установлен на tableOne.objectId
и tableOne.eventTime
(по существу, вручную создавая индекс). Тем не менее, я думал, что сначала найду опыт других людей, чтобы убедиться, что это лучший способ.
Спасибо большое!
Update:
Вот некоторые процедуры для загрузки данных тестирования и сравнения результатов:
drop procedure if exists populate_table_two;
delimiter #
create procedure populate_table_two(IN numRows int)
begin
declare v_counter int unsigned default 0;
while v_counter < numRows do
insert into tableTwo (eventTime)
values (CURRENT_TIMESTAMP - interval 0 + floor(0 + rand()*1000) minute);
set v_counter=v_counter+1;
end while;
end #
delimiter ;
drop procedure if exists populate_table_one;
delimiter #
create procedure populate_table_one
(IN numRows int, IN maxTableTwoId int, IN maxObjectId int)
begin
declare v_counter int unsigned default 0;
while v_counter < numRows do
insert into tableOne (tableTwoId, objectId)
values (floor(1 +(rand() * maxTableTwoId)),
floor(1 +(rand() * maxObjectId)));
set v_counter=v_counter+1;
end while;
end #
delimiter ;
Вы можете использовать их как следует заполнять 10000 строк в tableTwo
и 20000 строк в tableOne
(с случайные ссылки на tableOne
и случайные objectId
с между 1 и 5), что заняло соответственно 26,2 и 70,77 секунды:
call populate_table_two(10000);
call populate_table_one(20000, 10000, 5);
Update 2 (Испытано Срабатывание SQL):
Ниже испытанный SQL на основе инициирующего метода daniHp в. Это удерживает dateTime
в синхронизации на tableOne
при добавлении tableOne
или обновляется tableTwo
. Кроме того, этот метод также должен работать для отношений «многие ко многим», если столбцы условий копируются в таблицу соединений. При тестировании 300 000 строк в tableOne
и 200 000 строк в tableTwo
скорость старого запроса с аналогичными ограничениями составляла 0,12 с, а скорость нового запроса по-прежнему показывалась как 0,00 секунды. Таким образом, есть явное улучшение, и этот метод должен хорошо работать в миллионы строк и дальше.
alter table tableOne add column tableTwo_eventTime datetime;
create index ind_t1_oid_t2et on tableOne (objectId, tableTwo_eventTime);
drop TRIGGER if exists t1_copy_t2_eventTime;
delimiter #
CREATE TRIGGER t1_copy_t2_eventTime
BEFORE INSERT ON tableOne
for each row
begin
set NEW.tableTwo_eventTime = (select eventTime
from tableTwo t2
where t2.id = NEW.tableTwoId);
end #
delimiter ;
drop TRIGGER if exists upd_t1_copy_t2_eventTime;
delimiter #
CREATE TRIGGER upd_t1_copy_t2_eventTime
BEFORE UPDATE ON tableTwo
for each row
begin
update tableOne
set tableTwo_eventTime = NEW.eventTime
where tableTwoId = NEW.id;
end #
delimiter ;
И обновленный запрос:
select * from tableOne t1
inner join tableTwo t2 on t1.tableTwoId = t2.id
where t1.objectId = 1
order by t1.tableTwo_eventTime desc limit 0,10;
Вы можете создать другую агрегированную таблицу. – anttir
@anttir: Есть ли причина, которая предпочтительнее для репликации данных в одной из существующих таблиц? – Briguy37
[Пример кода] (http://sscce.org/) (здесь, в форме SQL) более полезен, чем специальная схема. – outis