2011-11-07 9 views
11

W ostatnich dniach zauważyłem coś dziwnego optymalizującego moje zapytanie. Mam proste kwerendy, która robi coś takiego:MYSQL - NOT vs var = false

SELECT id,name,amount FROM reservations WHERE NOT canceled ORDER BY name ASC 

zauważyłem mysql nie używał żadnego indeksu, więc zacząłem robić jakieś eksperymenty. Przypadkowo zastąpiłem "NOT cancelled" przez "canceled = false", a następnie Mysql zaczął używać "canceled" jako indeksu. Potem próbowałem za pomocą przeciwnego:

SELECT ... FROM reservations WHERE canceled ORDER BY ... 

sam wynik! Kiedy zmienię to na "canceled = true", indeks działa ponownie.

Moje pytanie brzmi: JAK PRZYJDŹ ?! Nie używa "NIE" w "elegancki" sposób? W każdym razie nie spodziewałem się, że to zrobi jakąkolwiek różnicę.

Używam InnoDB jako silnika, ale otrzymuję ten sam wynik za pomocą MyISAM. Czy ktoś może to wyjaśnić? Dzięki.

Edycja: Struktura Tabela

CREATE TABLE `reservations` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `trip_code` varchar(10) DEFAULT NULL, 
    `departure_date` date DEFAULT NULL, 
    `amount` float DEFAULT NULL, 
    `name` varchar(45) DEFAULT NULL, 
    `canceled` tinyint(1) NOT NULL DEFAULT '0', 
    `created_date` date NOT NULL, 
    `creator_user` int(11) NOT NULL DEFAULT '1', 
    `last_update_user` int(11) NOT NULL DEFAULT '1', 
    PRIMARY KEY (`id`), 
    KEY `trip_code` (`trip_code`), 
    KEY `departure_date` (`departure_date`), 
    KEY `created_date` (`created_date`), 
    KEY `canceled` (`canceled`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=123181 ; 
+0

której wersji używasz? – neworld

+0

Wersja serwera: 5.1.43-społeczność – Phoenix

+0

Czy możesz opublikować DDL tabeli. –

Odpowiedz

2

nie jestem zaznajomiony z MySQL, ale myśląc logicznie, rozumiem to tak:
Index jest jak książkę telefoniczną, gdy szukasz "Cohen", możesz od razu zacząć.
Ale jeśli szukasz NOT "Cohen", będziesz musiał przejechać każdy wpis i sprawdzić, czy różni się od "Cohen".
Tak więc, gdy szukasz określonej wartości, wygląda na to. I kiedy używasz NOT, szuka innej wartości, która zmieści się wewnątrz tinyint(1) (jak rozumiem, jest to nie tylko 1 lub 0, prawda?).

+0

Myślę, że wskazują one w pytaniu, że "WHERE cancelled" nie używa też indeksu. Chociaż nie jest jasne, o czym mówi "ten sam wynik". –

+0

Cóż, myślę, że "WHERE cancelled" jest prawdziwe, gdy wartość 'canceled 'wynosi 1, 2 lub 3 ... Więc nadal nie jest ** konkretną wartością ** orzecznikiem –

+0

Zignorowanie faktu, że kolumna jest zadeklarowana jako 'tinyint (1)' taka sama niespecyfika byłaby w przypadku 'WHERE canceled = true'. –

3

Mimo to przy użyciu indeksu, indeks (wierzcie lub nie) może mieć zapytanie wolniej. To trochę dziwne, ale wiąże się z selektywnością indeksu. Zwykle jest on przedstawiony w kolumnach typu boolean.

To descrbed jak:..

„Jak różne wartości pola są To jest liczbą od 0-1, choć można też myśleć o nim jako procent wartości 1 lub 100% oznacza, że ​​każda wartość w polu jest wyjątkowy”

ważne jest, aby rozważyć poniewaz:

"MySQL ma optymalizator kosztowy. Oznacza to, że MySQL oblicza koszt różnych sposobów wykonania zapytania, a następnie wybiera najtańszą cenę. Obliczanie kosztów jest niedokładną nauką. Więc oszacowanie zostanie podjęta, a szacunek jest źle czasami „

Zwykły prosty.

Jeśli dane szukasz ma mniej więcej 20% tej samej wartości (na przykład , anulowane ma 40% tabeli), a następnie, to proste, aby po prostu zrobić skan tabeli

EDIT:.

Odnośnie twojego pytania, wyjaśniam mówi, że MySQL jest usin g indeks. Ale może to nie być dobre, jedynym sposobem, aby zauważyć, że twoja optymalizacja jest lepsza, jest testowanie wydajności. Należy również wziąć pod uwagę koszt operacji INSERT, UPDATE i DELETE, aby zachować ten indeks. Zrób profilowanie zi bez indeksu.

Spójrz na to:

+0

Dzięki za odpowiedź. Rozumiem to teraz. Bez względu na to pytanie wciąż pozostaje pytanie. dlaczego "NIE var1" różni się od "var1 = false"? – Phoenix

+0

Masz rację, przepraszam. Zmieniłem odpowiedź. – santiagobasulto

1
SELECT * 
FROM 
(SELECT 1 AS C, 0 AS X UNION ALL 
SELECT 2 AS C, 1 AS X UNION ALL 
SELECT 3 AS C, 2 AS X) T 
WHERE X=true 

Zwraca

'2', '1' 

I

SELECT * 
FROM 
(SELECT 1 AS C, 0 AS X UNION ALL 
SELECT 2 AS C, 1 AS X UNION ALL 
SELECT 3 AS C, 2 AS X) T 
WHERE X 

Zwraca

'2', '1' 
'3', '2' 

Wygląda to tak, że w pierwszym przypadku true zostanie oddane do int, a następnie użyte w predykacie nadającym się do rozpoznania, natomiast w drugim przypadku wartość kolumny jest niejawnie rzutowana. Niejawne odlewy na ogół sprawiają, że stan jest nieopłacalny.

Patrząc na planie wyjaśnienia na zapytania z WHERE canceled = true daje

+----+-------------+--------------+------+---------------+----------+---------+-------+------+-----------------------------+ 
| id | select_type | table  | type | possible_keys | key | key_len | ref | rows |   Extra   | 
+----+-------------+--------------+------+---------------+----------+---------+-------+------+-----------------------------+ 
| 1 | SIMPLE  | reservations | ref | canceled  | canceled |  1 | const | 1 | Using where; Using filesort | 
+----+-------------+--------------+------+---------------+----------+---------+-------+------+-----------------------------+ 

Zważywszy na WHERE canceled masz

+----+-------------+--------------+------+---------------+-----+---------+-----+------+-----------------------------+ 
| id | select_type | table  | type | possible_keys | key | key_len | ref | rows |   Extra   | 
+----+-------------+--------------+------+---------------+-----+---------+-----+------+-----------------------------+ 
| 1 | SIMPLE  | reservations | ALL |    |  |   |  | 2 | Using where; Using filesort | 
+----+-------------+--------------+------+---------------+-----+---------+-----+------+-----------------------------+ 

Wygląda więc na to, że nie można nawet rozważyć indeks na canceled jako możliwa opcja w tym przypadku.