2010-11-03 8 views
9

Mam tabeli MySQL:Czy indeks MySQL może wykorzystywać indeks w zapytaniu o zakres, z zamówieniem?

CREATE TABLE mytable (
    id INT NOT NULL AUTO_INCREMENT, 
    other_id INT NOT NULL, 
    expiration_datetime DATETIME, 
    score INT, 
    PRIMARY KEY (id) 
) 

trzeba uruchomić zapytanie w formie:

SELECT * FROM mytable 
WHERE other_id=1 AND expiration_datetime > NOW() 
ORDER BY score LIMIT 10 

Jeśli dodać ten indeks mojatabela:

CREATE INDEX order_by_index 
ON mytable (other_id, expiration_datetime, score); 

Czy MySQL być w stanie użyć całego order_by_index w powyższym zapytaniu?

Wydaje się, że powinna być w stanie, ale według MySQL documentation: "indeksu może być również używany, nawet jeśli ORDER BY nie pasuje indeks dokładnie, jak długo wszystkich nieużywanych częściach index i wszystkie dodatkowe kolumny ORDER BY są stałe w klauzuli WHERE: "

Powyższy fragment wydaje się sugerować, że indeks byłby używany tylko w zapytaniu stałym, podczas gdy mój jest zapytaniem o zakres.

Czy ktoś może wyjaśnić, czy indeks byłby używany w tym przypadku? Jeśli nie, to w jaki sposób mogę wymusić użycie indeksu?

Dzięki.

Odpowiedz

8

MySQL użyje indeksu do zaspokojenia klauzuli WHERE, i użyje filesort zamówić wyników.

Nie można użyć indeksu dla zamówienia, ponieważ nie porównuje się wartości expiration_datetime ze stałą. Dlatego zwracane wiersze nie zawsze mają wspólny prefiks w indeksie, więc indeksu nie można użyć do sortowania.

Na przykład, należy rozważyć przykładowy zestaw rekordów 4 indeks dla tabeli:

a) [1,'2010-11-03 12:00',1] 
b) [1,'2010-11-03 12:00',3] 
c) [1,'2010-11-03 13:00',2] 
d) [2,'2010-11-03 12:00',1] 

Jeśli uruchomić zapytanie w 2010-11-03 11:00, to będzie zwracać wierszy A, C, D które nie są kolejne w indeksie. Tak więc MySQL musi wykonać dodatkowe przejście, aby posortować wyniki i nie może w tym przypadku użyć indeksu.

+1

>> Nie można użyć indeksu do porządku przez, ponieważ nie jesteś porównując expiration_datetime do stałej. << Myślę, że tutaj jest błąd. Dobrze wyjaśniłeś przyczynę tego w drugiej części odpowiedzi. – kellogs

3

Wygląda na to, że już sprawdziłeś dokumentację i ustawiono indeks. Użyj wyjaśnić i zobaczyć ...

EXPLAIN SELECT * FROM mytable 
WHERE other_id=1 AND expiration_datetime > NOW() 
ORDER BY score LIMIT 10 
7

Czy ktoś może wyjaśnić, czy indeks byłby używany w tym przypadku? Jeśli nie, to w jaki sposób mogę wymusić użycie indeksu?

Masz zasięg w stanie filtrowania, a ORDER BY nie pasuje do zakresu.

Te warunki nie mogą być obsługiwane z jednym indeksem.

Aby wybrać indeks stworzyć, trzeba uruchomić te pytania

SELECT COUNT(*) 
FROM mytable 
WHERE other_id = 1 
     AND (score, id) < 
     (
     SELECT score, id 
     FROM mytable 
     WHERE other_id = 1 
       AND expiration_datetime > NOW() 
     ORDER BY 
       score, id 
     LIMIT 10 
     ) 

i

SELECT COUNT(*) 
FROM mytable 
WHERE other_id = 1 
     AND expiration_datetime >= NOW() 

i porównanie ich wyników.

Jeśli druga kwerenda daje o tej samej lub więcej wartości niż pierwsza, należy użyć indeksu na (other_id, score) (i niech filtrować na expiration_datetime).

Jeśli drugie zapytanie daje znacznie mniej wartości niż pierwsze, należy użyć indeksu na (other_id, expiration_datetime) (i pozwolić na sortowanie na score).

Ten artykuł może być interesujące dla ciebie:

+0

Aby sprawdzić selektywność, należy sprawdzić różne wartości, więc nie zapomnij o COUNT DISTINCT. –

+0

@AllTheCode: 'COUNT DISTINCT' na czym? – Quassnoi