【问题标题】:How to get SQL SELECT to only show columns that triggered results in WHERE clause如何让 SQL SELECT 仅显示在 WHERE 子句中触发结果的列
【发布时间】:2019-03-25 17:43:11
【问题描述】:

是否有一种“高效”(在单个语句中)方式让 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

【问题讨论】:

  • 请选择对您最有帮助的答案并采纳!

标签: mysql sql select case union


【解决方案1】:

您可以使用case 表达式将条件移动到select

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

【讨论】:

  • 哇!感谢您闪电般的快速回复。我将研究 CASE 函数。以上会被认为是有效的吗?似乎非常重复,因为我将在 IN 子句中有很多值。另外:您不仅在移动,而且还从 WHERE 重复它。
  • 这是我现在最终使用并检查过的有效解决方案。但是,Bill 在下面表明 UNION 在这方面同样“有效”,甚至更“有效”。
  • @HappyBear 。 . .如果每列都有索引,UNION ALL 可能会更有效。如果没有索引,这应该更有效。
【解决方案2】:

尝试使用 CASE 语句。

CASE WHEN idx1 IN (1,2,3) Then
    idx1
ELSE
    NULL
END AS MyColumnName

【讨论】:

【解决方案3】:

您可以将查询一分为二,然后将它们联合起来:

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 来完成,但您担心要明确在每个条件下找到了哪些行。

上面的解决方案应该满足您将“其他”列显示为 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+ 行填充它,idx1 和 idx2 的随机值介于 0 和 50 之间。

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

【讨论】:

  • OP 明确排除 UNION 作为选项,您可以在问题中阅读。
  • 他们没有排除 UNION 作为选项。他们说他们知道这可以通过 UNION 来完成。但他们可能忽略了在每个选择列表中显式使用 NULL。
  • 你是对的。但 UNION 的效率不如其他解决方案。删除了我的反对票。
  • 真的吗?使用 UNION 意味着如果列 idx1 上有一个索引,而列 idx2 上有另一个索引,那么这两个索引都将被使用。这是在引用不同列的两个术语之间使用OR 优化条件的常用方法。尝试将它们组合到一个查询中会导致表扫描。
  • 如果 ORDER BY 用于读取行的索引顺序与您希望返回数据的顺序相同,则可以跳过文件排序。 GROUP BY 可能会跳过临时表,如果查询以索引顺序读取行,自然对行进行分组。就像您按组COUNT() 一样,并且确保按组顺序读取行,它不必保留临时表进行统计。一些其他类型的聚合可能仍需要使用临时表(如AVG())。这一切都与使用ORUNION 无关。
【解决方案4】:

我愿意:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-07
    • 2012-12-14
    • 2020-01-28
    相关资源
    最近更新 更多