考虑以下架构和查询:
CREATE TABLE composer(
cid INTEGER PRIMARY KEY,
cname TEXT
);
CREATE TABLE album(
aid INTEGER PRIMARY KEY,
aname TEXT
);
CREATE TABLE track(
tid INTEGER PRIMARY KEY,
cid INTEGER REFERENCES composer,
aid INTEGER REFERENCES album,
title TEXT
);
CREATE INDEX track_i1 ON track(cid);
CREATE INDEX track_i2 ON track(aid);
SELECT DISTINCT aname
FROM album, composer, track
WHERE cname LIKE '%bach%'
AND composer.cid=track.cid
AND album.aid=track.aid;
该模式适用于(简化的)音乐目录应用程序,但在其他情况下也会出现类似类型的模式。有大量的专辑。每张专辑都包含一首或多首曲目。每首曲目都有一个作曲家。每个作曲家可能与多个曲目相关联。
该查询要求提供每张专辑的名称,该专辑包含一首曲目,其作曲家的名字与“%bach%”匹配。
查询规划器需要为此查询在几种替代算法中进行选择。最佳选择取决于 表达式 "cname LIKE '%bach%'" 过滤结果的能力。让我们给这个表达式一个“过滤值”,它是一个介于 1.0 和 0.0 之间的数字。值 1.0 表示 cname LIKE '%bach%' 对于作曲家表中的每一行都是正确的。值 0.0 表示 表达式 永远不会为真。
当前查询计划器(3.8.0 版)假定过滤器值为 1.0。换句话说,它假定 表达式 始终为真。计划者假设最坏情况,因此它将选择一个最小化最坏情况运行时间的计划。这是一种安全的方法,但不是最优的。为 1.0 过滤器选择的计划是 track-album-composer。这意味着“轨道”表在外循环中。对于每一行曲目,在专辑中进行索引查找。然后在 composer 上进行索引查找,然后运行 LIKE 表达式 以查看是否应该输出专辑名称。
一个更好的计划是轨道作曲家专辑。如果 LIKE 表达式 为假,则第二个计划避免查找专辑。如果过滤器值略小于 1.0,则当前规划器将选择第二种算法。说 0.99。换句话说,如果计划者认为每 100 行中有 1 行的 LIKE 表达式 为假,那么它将选择第二个计划。当过滤器值很大时,这是正确(最快)的选择。
但在音乐库的常见情况下,过滤器值可能比 1.0 更接近 0.0。换句话说,字符串“bach”不太可能出现在大多数作曲家的名字中。对于接近 0.0 的值,最好的计划是作曲家-曲目-专辑。 composer-track-album 计划是扫描作曲家表一次以查找匹配“%bach%”的条目,并为每个匹配条目使用索引来查找曲目,然后查找专辑。当前的 3.8.0 查询规划器选择这个过滤值小于0.1左右时的第三个方案。