I have a table with following columns:
CREATE TABLE some_table (
col_1 ...
col_2 ...
col_3 ...
col_4 ...
col_5 ...
# some more columns
) ENGINE = InnoDB DEFAULT CHARSET = utf8 DEFAULT COLLATE utf8_unicode_ci;
and I perform the following search:
SELECT * FROM some_table t
WHERE (t.col_1 LIKE :col1
OR t.col_2 = :col2
OR t.col_3 = :col3
OR t.col_4 = :col4)
AND t.col_5 > :col5;
I populate :col1
, :col2
, etc. within the code. So ideally my index would be:
ALTER TABLE some_table
ADD INDEX my_index (col_1, col_2, col_3, col_4, col_5);
However, a new requirement showed up, and when :col1
, col2
, etc. are NULL
I need to skip them. Two alternatives are present:
- Make the SQL Query dynamic with multiple indexes:
I can query like:
SELECT * FROM some_table t
WHERE (t.col_1 LIKE :col1
-- OR t.col_2 LIKE :col2 -- Remove these columns from query if they're null
-- OR t.col_3 LIKE :col3 -- Remove these columns from query if they're null
OR t.col_4 LIKE :col4)
AND t.col_5 LIKE :col5;
but then I would need a lot more indexes because an index with col_1
, col_2
, col_3
, col_4
, col_5
would cover say a col_1
, col_2
, col_3
query but not col_1
, col_3
, col_4
query as per here and here. So my indexes become:
ALTER TABLE some_table
ADD INDEX index_1 (col_1, col_2, col_3, col_4, col_5),
ADD INDEX index_2(col_1, col_2, col_3, col_5),
ADD INDEX index_3(col_1, col_2, col_4, col_5),
ADD INDEX index_4(col_1, col_3, col_4, col_5),
ADD INDEX index_5(col_2, col_3, col_4, col_5),
ADD INDEX index_6 (col_1, col_2, col_5),
ADD INDEX index_7 (col_1, col_3, col_5),
ADD INDEX index_8 (col_1, col_4, col_5),
ADD INDEX index_9 (col_2, col_3, col_5),
ADD INDEX index_10 (col_2, col_4, col_5),
ADD INDEX index_11 (col_3, col_4, col_5),
ADD INDEX index_12 (col_1, col_5),
ADD INDEX index_13 (col_2, col_5),
ADD INDEX index_14 (col_3, col_5),
ADD INDEX index_15(col_4, col_5),
ADD INDEX index_16 (col_5);
I do not really care about my read performance, but a lot of storage overhead might be an issue in the long term.
- Second alternative: Keep the single index (
my_index
) as is, but change query:
SELECT * FROM some_table t
WHERE ((t.col_1 IS NOT NULL AND t.col_1 LIKE :col1)
OR (t.col_2 IS NOT NULL AND t.col_2 LIKE :col2)
OR (t.col_3 IS NOT NULL AND t.col_3 LIKE :col3)
OR (t.col_4 IS NOT NULL AND t.col_4 LIKE :col4))
AND t.col_5 LIKE :col5;
I don't know if it would be enough to trick MySQL to use the index because if say :col1
is indeed NULL
the condition becomes: t.col_2 IS NOT NULL AND t.col_2 LIKE NULL
and since this is an impossible condition, the optimizer could remove it.
With the data currently in hand, both queries run about the same time. What is the correct way to proceed with this?