我有一个带有
OR
的查询,运行效果很差(即成本为 138 个单位):
SELECT *
FROM Customers
WHERE (FirstName LIKE 'Ian%') OR (LastName LIKE 'Boyd%')
但是返回相同结果的这个查询运行得更好(即 0.6 个单位):
SELECT *
FROM Customers
WHERE (FirstName LIKE 'Ian%')
UNION
SELECT *
FROM Customers
WHERE (LastName LIKE 'Boyd%')
从我向关系数据库引擎请求什么信息的角度来看,这两个查询在逻辑上是等价或相同的。但如果是这样的话,现代复杂的查询优化器应该理解这一切,并且不会以任何不同的方式运行查询。但事实确实如此;我们知道我们都可以将其归因于古怪的查询优化器。所以也许它们不等价。
这两个数据库查询在逻辑上是等价的吗?
CREATE TABLE Customers
(
FirstName varchar(50) NOT NULL,
LastName varchar(50) NOT NULL
)
CREATE INDEX IX_Customers_FirstName ON Customers (FirstName);
CREATE INDEX IX_Customers_LastNmae ON Customers (LastName);
假设
customers
没有重复行,两者在逻辑上是相同的。这是一个合理的假设。
UNION
版本更快,可能是因为 SQL 引擎可以使用 LIKE
模式的索引——它们不以通配符开头。
更快且几乎等效的版本是:
SELECT *
FROM Customers
WHERE FirstName LIKE 'Ian%'
UNION ALL
SELECT *
FROM Customers
WHERE LastName LIKE 'Boyd%' AND FirstName NOT LIKE 'Ian%';
这里唯一的问题是
FirstName
是否为 NULL
。在这种情况下,逻辑甚至会过滤掉匹配的姓氏。完全等效的需要考虑到这一点:
SELECT *
FROM Customers
WHERE FirstName LIKE 'Ian%'
UNION ALL
SELECT *
FROM Customers
WHERE LastName LIKE 'Boyd%' AND
(FirstName NOT LIKE 'Ian%' OR FirstName IS NULL);
这些版本应该更快,因为它们使用
UNION ALL
而不是 UNION
。后者会产生删除重复项的开销。但是,WHERE
子句会删除这些重复项,而无需跨行查看。
这两个查询的形式通常不具有相同的语义——尽管可能需要更复杂的示例来演示不同的结果。
第一种形式(带有
OR
)有 SELECT ...
但没有 SELECT DISTINCT ...
。因此它可能会产生重复的行。 (请参阅该链接中的参考文献 5、6。)
第二种形式有
... UNION ...
但没有 ... UNION ALL ...
。因此它一定不会产生重复的行,即使各个SELECT ...
不是DISTINCT
。
OTOH,如果这是您的特定 DBMS 上的特定模式及其特定配置的唯一区别,我预计
UNION
(不是 ALL
)形式的性能会更差,因为它需要重复数据删除。