Mam tabelę MySQL jak poniżej:
CREATE TABLE `dates` (
`id` int UNSIGNED NULL AUTO_INCREMENT ,
`object_id` int UNSIGNED NOT NULL ,
`date_from` date NOT NULL ,
`date_to` date NULL ,
`time_from` time NULL ,
`time_to` time NULL ,
PRIMARY KEY (`id`)
);
która jest najczęściej zadawana w ten sposób:
SELECT object_id FROM `dates`
WHERE NOW() BETWEEN date_from AND date_to
Jak najlepiej indeksować tabelę? Czy powinienem utworzyć dwa indeksy, jeden dla date_from
i jeden dla date_to
lub czy połączony indeks w obu kolumnach jest lepszy?
Odpowiedzi:
4 dla odpowiedzi № 1Dla zapytania:
WHERE NOW() >= date_from
AND NOW() <= date_to
Indeks złożony (date_from, date_to)
jest bezużyteczny.
Utwórz oba indeksy: (date_from)
i (date_to)
i niech za każdym razem decyduje optymalizator SQLjeden do użycia. W zależności od wartości i selektywności optymalizator może wybrać jeden lub drugi indeks. Ani żadnego z nich. Nie ma łatwego sposobu na stworzenie indeksu, który uwzględni oba warunki.
(W celu zoptymalizowania takiego warunku można użyć indeksu przestrzennego, jeśli można przetłumaczyć daty na szerokość i długość geograficzną).
Aktualizacja
Mój błąd. Indeks na (date_from, date_to, object_id)
może i jest rzeczywiście używany w niektórych sytuacjach dla tego zapytania. Jeśli selektywność NOW() <= date_from
jest wystarczająco wysoki, optymalizator zdecyduje się na toindeksu, niż wykonanie pełnego skanu na stole lub użycie innego indeksu. Dzieje się tak, ponieważ jest to indeks obejmujący, co oznacza, że nie trzeba pobierać żadnych danych z tabeli, wymagane jest tylko odczytanie z danych indeksu.
Niewielka uwaga (niezwiązana z wydajnością, tylko poprawność zapytania). Twój stan jest równoważny z:
WHERE CURRENT_DATE() >= date_from
AND ( CURRENT_DATE() + INTERVAL 1 DAY <= date_to
OR ( CURRENT_DATE() = NOW()
AND CURRENT_DATE() = date_to
)
)
Czy na pewno tego chcesz, czy chcesz tego:
WHERE CURRENT_DATE() >= date_from
AND CURRENT_DATE() <= date_to
The NOW()
funkcja zwraca a DATETIME
, podczas CURRENT_DATE()
zwraca a DATE
, bez części czasu.
2 dla odpowiedzi nr 2
Powinieneś utworzyć indeks obejmujący date_from,date_to i object_id jak wyjaśniono przez ypercube. Kolejność pól w indeksie zależy od tego, czy będziesz mieć więcej danych dotyczących przeszłości czy przyszłości. Jak zauważył Erwin w odpowiedzi na komentarz Sanjay, pole date_to będzie bardziej selektywne, jeśli masz więcej dat w przeszłości i na odwrót.
CREATE INDEX ON (date_to, date_from, object_id);
1 dla odpowiedzi nr 3
Ile wierszy odpowiada rozmiarowi twojego stołuzapytanie o zwrot? Jeśli jest to więcej niż 10 procent, nie zawracałbym sobie głowy tworzeniem indeksu, w takim przypadku twój byłby prawie tak blisko skanowania tabeli. Jeśli jest to znacznie poniżej 10 procent, to w tym przypadku użyłby indeksu zawierającego (date_from, date_to, object_id), więc wynik zapytania może być zbudowany w całości z informacji w indeksie, bez bazy danych havind, aby śledzić z powrotem do danych tabeli, aby uzyskać wartość dla id_obiektu.
W zależności od wielkości stołu może to zająć dużo miejsca. Jeśli możesz oszczędzić, spróbuj.
0 dla odpowiedzi nr 4
Utwórz indeks z opcją (date_from, date_to), ponieważ ten pojedynczy indeks może być użyty dla kryteriów WHERE
Jeśli utworzysz oddzielne indeksy, MySQL będzie musiał użyć jednego lub drugiego zamiast obu