Рассмотрим структуру, в которой у вас есть отношение многие-к-одному (или один-ко-многим) с условием (где, порядок и т. Д.) В обеих таблицах.Например:
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
.Если вы объясните запрос, приведенный выше, он покажет «Использование сортировки файлов».По сути, он сначала применяет индекс tableOne.objectId
, но не может применять индекс tableTwo.eventTime
, потому что этот индекс предназначен для всей таблицы tableTwo (не для ограниченного набора результатов), и, следовательно, он должен выполнять сортировку вручную.
Таким образом, существует ли способ создания индекса между таблицами, чтобы не приходилось сортировать файлы при каждом получении результатов? Что-то вроде:
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
(по сути, создание индекса вручную).Тем не менее, я подумал, что сначала должен найти опыт других людей, чтобы убедиться, что это лучший способ.
Большое спасибо!
Обновление:
Вот несколько процедур для загрузки данных испытаний и сравнения результатов:
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);
Обновление 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;