Как заставить SQL SELECT отображать только те столбцы, которые вызвали результаты в предложении WHERE - PullRequest
0 голосов
/ 25 марта 2019

Существует ли " эффективный " (в одном выражении) способ заставить MySQL отображать только те столбцы, которые вызвали результаты в предложении WHERE при использовании нескольких условий?
Я знаю, что это может бытьсделано с помощью объединения UNION операторов SQL.Я хочу знать конкретно, какой столбец вызвал результат, так как оба столбца отображаются, когда любое условие возвращает true.Например:

Table:
idx1       idx2
1          2
5          6

sql statement: SELECT idx1, idx2 FROM myTable WHERE idx1 IN(1,2,3) OR idx2 IN(4,5,6)

Очевидно, оба столбца возвращаются в вышеуказанных 2 строках , поскольку для каждого выполняется только одно из условий. Есть ли способ получить:

idx1       idx2
1          NULL
NULL       6

Ответы [ 4 ]

2 голосов
/ 25 марта 2019

Вы можете переместить условия в select, используя выражение case:

SELECT (CASE WHEN idx1 IN (1, 2, 3) THEN idx1 END) as idx1,
       (CASE WHEN idx2 IN (4, 5, 6) THEN idx2 END) as idx2
FROM myTable
WHERE idx1 IN (1,2,3) OR idx2 IN (4, 5, 6);
1 голос
/ 25 марта 2019

Вы можете разбить запрос на две части и объединить их вместе:

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);

Редактировать: Я знаю, что вы сказали, что знали, что это можно сделать с помощью UNION, но вам было важно выяснить, какие строки были найдены при каждом условии.

Приведенное выше решение должно удовлетворить ваше требование, чтобы столбец "other" отображался как NULL, потому что мы можем явно указать NULL в списке выбора вместо того, чтобы называть другой столбец.

Вы должны определить псевдонимы столбцов в первом запросе в UNION. Имена столбцов последующих запросов в UNION будут использовать псевдонимы столбцов, определенные в первом запросе.


Комментарии от @Kaii:

Оптимизация слияния индексов не помогает так часто, как вы могли ожидать. Вот демоверсия, протестированная на 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`)
);

Я заполнил его 1M + строками, со случайными значениями от 0 до 50 для idx1 и idx2.

select count(*) from mytable;
+----------+
| count(*) |
+----------+
|  1048576 |
+----------+

Запрос, использующий OR, не выполняет слияние индексов, он выполняет сканирование таблицы (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 |
+----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+

Тогда как мое решение с UNION использует оба индекса и не использует временную таблицу:

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 |
+----+-------------+---------+------------+-------+---------------+------+---------+------+--------+----------+--------------------------+

Я профилировал оба запроса и обнаружил, что запрос UNION в этом случае немного быстрее.

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) |
+----------+------------+-----------------------------------------------------------------------------------------------------------------------------+
1 голос
/ 25 марта 2019

Попробуйте использовать оператор CASE.

CASE WHEN idx1 IN (1,2,3) Then
    idx1
ELSE
    NULL
END AS MyColumnName
0 голосов
/ 25 марта 2019

Я бы сделал:

select
  case when idx1 in (1, 2, 3) then idx1 end as idx1,
  case when idx2 in (4, 5, 6) then idx2 end as idx2
from t
...