Rozważ następujący schemat (dostępny na sqlFiddle)
create table ad (
id int primary key auto_increment,
category_id int,
city_id int,
name varchar(255),
key(category_id),
key(city_id)
);
create table category (
id int primary key auto_increment,
name varchar(255)
);
create table city (
id int primary key auto_increment,
name varchar(255)
);
insert into category values (null, "Category 1");
insert into category values (null, "Category 2");
insert into category values (null, "Category 3");
insert into city values (null, "City 1");
insert into city values (null, "City 2");
insert into ad values (null, 1, 1, "Category 1 city 1");
insert into ad values (null, 1, 2, "Category 1 city 2");
insert into ad values (null, 2, 1, "Category 2 city 1");
insert into ad values (null, 2, 2, "Category 2 city 2");
insert into ad values (null, 3, 1, "Category 3 city 1");
insert into ad values (null, 3, 2, "Category 3 city 2");
Podczas wykonywania prostego połączonego zapytania bez zamówienia według:
SELECT ad.id, ad.name, category.name, city.name FROM ad
INNER JOIN category ON category.id = ad.category_id
INNER JOIN city ON city.id = ad.city_id
wynik jest dość wydajny:
Jednak jak tylko dodam klauzulę ORDER BY, następuje tymczasowe sortowanie tabel i plików:
SELECT ad.id, ad.name, category.name, city.name FROM ad
INNER JOIN category ON category.id = ad.category_id
INNER JOIN city ON city.id = ad.city_id
ORDER BY ad.id
Jak zoptymalizować takie zapytanie?
Odpowiedzi:
0 dla odpowiedzi № 1Możesz użyć STRAIGTH_JOIN.
SELECT STRAIGHT_JOIN ad.id, ad.name, category.name, city.name FROM ad
INNER JOIN category ON category.id = ad.category_id
INNER JOIN city ON city.id = ad.city_id
ORDER BY ad.id
Optymalizator MySQL wybrał dostęp do tabeli w niewłaściwej kolejności (miasto, reklama, kategoria) optymalną kolejnością dostępu byłaby (reklama, kategoria, miasto) STRAIGTH_JOIN wymusiłby dostęp do tabeli.
0 dla odpowiedzi nr 2
Przedwczesna panika. Przy tak niewielu rzędach EXPLAIN
„t” nie udowodni, że plan zapytań nie jest dobry. Z tysiącem reklam i dziesiątkami miast i kategorii Optymalizator może wybrać ad
jako pierwszy stół do pracy.
Optymalizator również nie wie, czy Twoje tabele to 1 reklama - wiele kategorii i miast. Lub wielu: wielu.
Skarżyłeś się, bo wiesz, że każdy ad
jest tylko w jednej kategorii i jednym mieście?
„BNL” i „korzystanie z bufora dołączania” są dość wydajnymi sposobami wykonywania zapytań - ładują wszystko, a następnie manipulują nimi w wydajny sposób w pamięci RAM.
Ponadto „Korzystanie z plików tymczasowych” i „Korzystanie z plików” nie jest aż tak złe, jak się wydaje. To jest zazwyczaj wykonane w pamięci RAM za pomocą wydajnego „qsort” w pamięci.
-1 dla odpowiedzi nr 3
Sam udało mi się znaleźć rozwiązanie. Przede wszystkim należy skutecznie obliczyć wymagane identyfikatory z głównej tabeli (ich filtrowanie i sortowanie), a następnie po prostu ponownie połączyć wyniki z tymi kluczami w wewnętrznym zapytaniu:
SELECT ad.id, ad.name, category.name, city.name FROM
(
SELECT id FROM ad WHERE price <= 3000 ORDER BY id DESC
) AS v
JOIN ad ON v.id = ad.id
JOIN category ON category.id = ad.category_id
JOIN city ON city.id = ad.city_id
Jest oczywiście więcej kolumn zaangażowanych w moją pracęwalizka. Przy takim zapytaniu (zawierającym 60 000 rekordów w głównej tabeli) szybkość wykonywania wzrosła z 0,16 do 0,004 s, o ile posortuję według indeksowanych kolumn.