我正在使用SQL Server 2014,并且具有如下表
CREATE TABLE t_priority (
Name VARCHAR(50) UNIQUE,
Priority INT NOT NULL
);
我希望从此表中找到所有优先级低于与以'foo'开头的名称相关的最低优先级的记录。
这是我目前的查询
select t1.Name from t_priority t1 join
(select min(Priority) as Priority, Name from t_priority where Name like 'foo%' ) t2
on t1.Name != t2.Name
and t1.Priority < t2.Priority;
令我困扰的一件事是,我对表进行了两次全扫描(对于连接的每一条都进行一次全扫描?至少进行了一次物理读取全扫描,一次进行了逻辑全扫描?)。我想应该有一种方法可以一次扫描。
是否可以在一次扫描中获得与以上相同的查询结果?我觉得索引的排序和快速访问将很有用,所以优先级上的索引?对于我得到的所有优先级等于参数值的所有名称的其他查询,这也将很有用
在Name上过滤索引似乎很有用,但条件太特殊,无法创建永久索引,并且在运行时创建寿命短的索引似乎代价昂贵(根据文档,过滤索引基于磁盘)
欢迎上述查询的其他改进:)
答案 0 :(得分:2)
您可以这样表示:
select t.*
from (select t.*,
min(case when name like 'foo%' then priority end) over () as min_foo_priority
from t_priority t
) t
where priority < min_foo_priority;
或者:
select t.*
from t_priority t
where t.priority < (select min(tt.priority) from t_priority tt where tt.name like 'foo%');
对于此版本,您可以在t_priority(name, priority)
上创建索引,查询可能可以使用该索引。
您的直觉认为您可以一次扫描即可完成此操作。如果对表的扫描使得最后一行是“ foo”的最低优先级的行,将会发生什么?在此之前,您没有足够的信息来接受或拒绝行。
您可能会很高效-但要使用索引。通配符“ foo”是有问题的,但是您可以使用持久化的计算列来处理。所以:
alter table t_priority add name3 as (left(name, 3)) persisted;
create index idx_priority_name3_priority on t_priority(name3, priority);
然后在查询中使用name3
。例如:
select t.*
from t_priority t
where t.priority < (select min(tt.priority) from t_priority tt where tt.name3 = 'foo');