Рассмотрим структуру, в которой у вас есть отношение много к одному (или один ко многим) с условием (где, порядок и т.д.) на обеих таблицах. Например:
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 ;
Вы можете использовать их следующим образом, чтобы заполнить 10 000 строк в tableTwo и 20 000 строк в tableOne (со случайными ссылками на tableOne и случайными objectId между 1 и 5), что заняло 26,2 и 70,77 секунд соответственно бежать за мной:
call populate_table_two(10000);
call populate_table_one(20000, 10000, 5);
Обновление 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;