You can split up the query into two, and UNION them together:
SELECT idx1, NULL AS idx2
FROM myTable
WHERE idx1 IN (1,2,3)
UNION ALL
SELECT NULL, idx2
FROM myTable
WHERE idx2 IN (4,5,6);
Edit: I know you said you knew it could be done with UNION, but you were concerned about making it clear which rows were found with each condition.
The solution above should satisfy your requirement that the "other" column is shown as NULL, because we can put NULL into the select-list explicitly instead of naming the other column.
You should define the column aliases in the first query in the UNION. Column names of subsequent queries in the UNION will use the column aliases defined in the first query.
Re comments from @Kaii:
Index merge optimization doesn't help as often as you might expect. Here's a demo, tested on MySQL 8.0.14:
CREATE TABLE `mytable` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`idx1` int(11) DEFAULT NULL,
`idx2` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`),
KEY `idx1` (`idx1`),
KEY `idx2` (`idx2`)
);
I filled it with 1M+ rows, with random values between 0 and 50 for idx1 and idx2.
select count(*) from mytable;
+----------+
| count(*) |
+----------+
| 1048576 |
+----------+
The query using OR
does not do an index-merge, it does a table-scan (type=ALL
):
explain SELECT idx1, idx2 FROM myTable WHERE idx1 IN(1,2,3) OR idx2 IN(4,5,6);
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | myTable | NULL | ALL | idx1,idx2 | NULL | NULL | NULL | 1046577 | 51.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+
Whereas my solution with UNION
does use both indexes, and does not use a temp table:
explain SELECT idx1, NULL AS idx2 FROM myTable WHERE idx1 IN (1,2,3) UNION ALL SELECT NULL, idx2 FROM myTable WHERE idx2 IN (4,5,6);
+----+-------------+---------+------------+-------+---------------+------+---------+------+--------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+------+---------+------+--------+----------+--------------------------+
| 1 | PRIMARY | myTable | NULL | range | idx1 | idx1 | 5 | NULL | 113264 | 100.00 | Using where; Using index |
| 2 | UNION | myTable | NULL | range | idx2 | idx2 | 5 | NULL | 112678 | 100.00 | Using where; Using index |
+----+-------------+---------+------------+-------+---------------+------+---------+------+--------+----------+--------------------------+
I profiled both queries, and find that the UNION
query is quite a bit faster in this case.
show profiles;
+----------+------------+-----------------------------------------------------------------------------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-----------------------------------------------------------------------------------------------------------------------------+
| 1 | 0.22477500 | SELECT idx1, idx2 FROM myTable WHERE idx1 IN(1,2,3) OR idx2 IN(4,5,6) |
| 2 | 0.05573200 | SELECT idx1, NULL AS idx2 FROM myTable WHERE idx1 IN (1,2,3) UNION ALL SELECT NULL, idx2 FROM myTable WHERE idx2 IN (4,5,6) |
+----------+------------+-----------------------------------------------------------------------------------------------------------------------------+