问题
I'm trying to optimise the following MySQL query
SELECT Hotel.HotelId, Hotel.Name, Hotel.Enabled, Hotel.IsClosed,
HotelRoom.HotelId, HotelRoom.RoomId, HotelRoom.Name AS RoomName
FROM Hotel
INNER JOIN
HotelRoom ON Hotel.HotelId = HotelRoom.HotelId
WHERE Hotel.IsClosed = 0
AND Hotel.Enabled = 1
AND HotelRoom.Deleted = 0
AND HotelRoom.Enabled = 1
AND IF(LENGTH(TRIM(sAuxiliaryIds)) > 0 AND sAuxiliaryIds IS NOT NULL,
FIND_IN_SET(Hotel.AuxiliaryId, sAuxiliaryIds), 1=1) > 0
ORDER BY Hotel.HotelId ASC, HotelRoom.RoomId ASC
The PRIMARY KEYS are Hotel.Hotel
and HotelRoom.RoomId
, and I've got a FOREIGN KEY from HotelRoom.HotelId to Hotel.HotelId.
Should I be creating a INDEX for (Hotel.IsClosed, Hotel.Enabled)
and (HotelRoom.Deleted, HotelRoom.Enabled)
which is used in the WHERE clause, and should this index include the PRIMARY key so for example I should create a INDEX for (Hotel.HotelId, Hotel.IsClosed, Hotel.Enabled)
EDIT 1
I've added the following in the WHERE statement AND IF(LENGTH(TRIM(sAuxiliaryIds)) > 0 AND sAuxiliaryIds IS NOT NULL, FIND_IN_SET(Hotel.AuxiliaryId, sAuxiliaryIds), 1=1) > 0
Should these also be included in INDEX
This is what the EXPLAIN statement is showing for this query
I added both INDEX suggestions but when I ran the EXPLAIN statement they both showed that no key was going to be used
回答1:
MySQL's Optimizer does not care which table comes first in a JOIN
. It will look at statistics (etc) to decide for itself whether to start with Hotel
or HotelRoom
. So, you should write indexes for both cases, so as not to restrict the Optimizer.
MySQL almost always performs a JOIN
by scanning one table. Then, for each row in that table, look up the necessary row(s) in the other table. See "Nested Loop Join" or "NLJ". This implies that the optimal indexes are (often) thus: For the 'first' table, columns of the WHERE
clause involving the first table. For the second
table, the columns from both the WHERE
and ON
clauses involving the second table.
Assuming that the Optimizer started with Hotel:
Hotel: INDEX(IsClosed, Enabled) -- in either order
HotelRoom: INDEX(Deleted, Enabled, HotelId) -- in any order
If it started with HotelRoom:
HotelRoom: INDEX(Deleted, Enabled) -- in either order
Hotel: PRIMARY KEY(HotelId) -- which you already have?
If there are a lot of closed/disabled hotels, then this may be beneficial:
Hotel: INDEX(IsClosed, Enabled, HotelId)
As Tim mentioned, it may be beneficial to augment an index to include the rest of the columns mentioned, thereby making the index "covering". (But don't do this with the PRIMARY KEY
or any UNIQUE
key.)
If you provide SHOW CREATE TABLE
and the sizes of the tables, we might have further suggestions.
回答2:
There are two potential indexing strategies here, depending on which of the two tables appears on the left side of the inner join (either table could potentially appear on either side of the join). Given that the HotelRoom
table likely contains many more records than the Hotel
table, I would suggest placing the Hotel
table on the left side of the join. This would imply that the Hotel
table would be scanned, and the index used for the join to HotelRoom
. Then, we can try using the following index on HotelRoom
:
CREATE INDEX hotel_room_idx ON HotelRoom (HotelId, Deleted, Enabled, Name, RoomId);
This should speed up the join substantially, covers the WHERE
clause, and also covers all columns in the select on HotelRoom
. Note that the following simplified index might also be very effective:
CREATE INDEX hotel_room_idx ON HotelRoom (HotelId, Deleted, Enabled);
This just covers the join and WHERE
clause, but MySQL might still choose to use it.
来源:https://stackoverflow.com/questions/57425866/what-index-should-i-use-when-using-join-on-primary-key