Hãy xem xét một cấu trúc mà bạn có mối quan hệ nhiều (một hoặc nhiều) với một điều kiện (ở đó, theo thứ tự, vv) trên cả hai bảng. Ví dụ:Có thể lập chỉ mục bảng chéo không?
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;
và cho một truy vấn Ví dụ:
select * from tableOne t1
inner join tableTwo t2 on t1.tableTwoId = t2.id
where objectId = '..'
order by eventTime;
Hãy nói rằng bạn chỉ số tableOne.objectId
và tableTwo.eventTime
. Nếu sau đó bạn giải thích về truy vấn trên, nó sẽ hiển thị "Using filesort". Về cơ bản, trước tiên nó áp dụng chỉ mục tableOne.objectId
, nhưng nó không thể áp dụng chỉ mục tableTwo.eventTime
vì chỉ mục đó dành cho toàn bộ tableTwo (không phải tập hợp kết quả giới hạn), và do đó nó phải thực hiện sắp xếp thủ công.
Do đó, có cách nào để thực hiện chỉ mục bảng chéo để không phải ghi lại mỗi lần kết quả được truy xuất không? Cái gì như:
create index ind_t1oi_t2et on tableOne t1
inner join tableTwo t2 on t1.tableTwoId = t2.id
(t1.objectId, t2.eventTime);
Ngoài ra, tôi đã nhìn vào việc tạo ra một cái nhìn và lập chỉ mục đó, nhưng chỉ mục không được hỗ trợ cho quan điểm.
Giải pháp tôi đã hướng đến nếu không thể lập chỉ mục bảng chéo là sao chép dữ liệu có điều kiện trong một bảng. Trong trường hợp này có nghĩa là eventTime
sẽ được nhân rộng trong tableOne
và chỉ mục nhiều cột sẽ được thiết lập trên tableOne.objectId
và tableOne.eventTime
(về cơ bản tạo thủ công chỉ mục). Tuy nhiên, tôi nghĩ tôi sẽ tìm kiếm trải nghiệm của người khác trước để xem đó có phải là cách tốt nhất hay không.
Cảm ơn nhiều!
Cập nhật:
Dưới đây là một số thủ tục cho các dữ liệu tải thử nghiệm và kết quả so sánh:
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 ;
Bạn có thể sử dụng các như sau để cư 10.000 hàng trong tableTwo
và 20.000 hàng trong tableOne
(với tham chiếu ngẫu nhiên đến tableOne
và ngẫu nhiên objectId
s trong khoảng từ 1 đến 5), mất 26,2 và 70,77 giây tương ứng để chạy cho tôi:
call populate_table_two(10000);
call populate_table_one(20000, 10000, 5);
Cập nhật 2 (Tested Triggering SQL):
Dưới đây là SQL thử và thử nghiệm dựa trên phương pháp kích hoạt daniHp của. Điều này giữ cho số dateTime
đồng bộ hóa trên tableOne
khi tableOne
được thêm hoặc tableTwo
được cập nhật. Ngoài ra, phương thức này cũng sẽ làm việc cho các mối quan hệ nhiều-nhiều nếu các cột điều kiện được sao chép vào bảng nối kết. Trong thử nghiệm của tôi 300.000 hàng trong tableOne
và 200.000 hàng trong tableTwo
, tốc độ của truy vấn cũ với giới hạn tương tự là 0,12 giây và tốc độ truy vấn mới vẫn hiển thị là 0,00 giây. Vì vậy, có một sự cải tiến rõ ràng, và phương pháp này nên thực hiện tốt vào hàng triệu hàng và xa hơn.
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 ;
Và truy vấn Cập nhật:
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;
Bạn có thể tạo một bảng tổng hợp khác. – anttir
@anttir: Có lý do nào thích hợp hơn khi sao chép dữ liệu trong một trong các bảng hiện có không? – Briguy37
[Mã mẫu] (http://sscce.org/) (ở đây, dưới dạng SQL) hữu ích hơn lược đồ đặc biệt. – outis