在 MySQL SELECT 语句中使用 OR 子句通常是个坏主意吗?

Is using OR clause in MySQL SELECT statement a generally bad idea?

前几天我遇到了查询问题。大型数据集大约需要 10 秒。查询看起来像这样:

SELECT a.* from Document as a
LEFT JOIN Waybill as b on a.waybill = b.id
WHERE a.enterpriseGuid = '763a3ac3-a3c7-4379-9735-2a4a96e87e5d'
OR b.enterpriseGuid = '763a3ac3-a3c7-4379-9735-2a4a96e87e5d'

这 运行 速度很慢。但是,然后我将其更改为:

SELECT a.* from Document as a
LEFT JOIN Waybill as b on a.waybill = b.id
WHERE a.enterpriseGuid = '763a3ac3-a3c7-4379-9735-2a4a96e87e5d'
UNION ALL
SELECT a.* from Document as a
LEFT JOIN Waybill as b on a.waybill = b.id
WHERE b.enterpriseGuid = '763a3ac3-a3c7-4379-9735-2a4a96e87e5d'

这大约用了 0.01 秒,虽然这两个查询产生的结果基本相同!我查找了官方 MySQL 文档,发现了一个有趣的评论 here:

Indices lose their speed advantage when using them in OR-situations (4.1.10):

SELECT * FROM a WHERE index1 = 'foo' UNION SELECT * FROM a WHERE index2 = 'baar';

is much faster than

SELECT * FROM a WHERE index1 = 'foo' OR index2 = 'bar';

所以,我的问题有 3 个部分:

OR 本身 并不坏。与 SQL 中的几乎所有其他构造一样,它可能是也可能不是一个好主意。

您发现优化器存在问题。 . .和许多数据库通用的一种。当您的 OR 条件来自不同的表时,优化器很难利用索引。

您改进的解决方案之所以有效,是因为每个子查询都可以利用索引。

您可能会发现以下版本比第一个版本好但比第二个版本差:

SELECT d.*
FROM Document d
WHERE d.enterpriseGuid = '763a3ac3-a3c7-4379-9735-2a4a96e87e5d' OR
      (EXISTS (SELECT 1
               FROM Waybill b
               WHERE d.waybill = b.id AND
                     b.enterpriseGuid = '763a3ac3-a3c7-4379-9735-2a4a96e87e5d'
              )
      );

这是一个与优化器相关的问题,因此 Engine/Version/Table 统计数据等可能会有所不同。

实际上你不能说全 table 扫描总是比两次索引扫描然后整理结果(即 union 运算符)差。这取决于指数的选择性。不过你还是要非常小心OR,这是真的。