这两个数据库查询在代数上是否相同?

问题描述

我想弄清楚这两个查询在逻辑上是否相同 - 从理论/布尔代数/关系演算的角度来看。

我有一个运行不佳的 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%')

现在在我看来,这两个查询在逻辑上是等效的或相同的 - 从我向关系数据库引擎询问什么信息的角度来看。但如果是这样的话,现代复杂的查询优化器应该理解所有这些,并且不会以任何不同的方式运行查询。然而它确实如此;我们知道我们都可以归结为古怪的查询优化器。

当然,除非它们实际上等效。

在这种情况下:我想知道:

查询 A查询 B

注意

这不是关于性能调优的问题,涉及 DDL、数据量或要求任何人来调优查询。如果现在数据库引擎运行第二个查询:那么我只需要忍受愚蠢的优化器。这是一个与语言无关、与数据库无关的理论问题。我不是在问如何清除过程缓存,我不是在问如何执行 WITH RECOMPILE。我不是要求解决问题。我在问一个理论问题。

解决方法

假设 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 ...。所以它might produce duplicate rows。 (请参阅该链接中的参考文献 5、6。)

第二种形式有 ... UNION ... 但没有 ... UNION ALL ...。所以它 must not produce duplicate rows,即使单个 SELECT ... 不是 DISTINCT

OTOH 如果这是您在具有特定配置的特定 DBMS 上的特定架构的唯一区别,我希望 UNION(而不是 ALL)表单的性能更差,因为它需要去重。