All*_*len 5 sql-server sql-server-2008-r2
我max在where子句中有一个带有 a 的查询,这很慢。
select count(*)
from TableName tbl1
where tbl1.id = (
select max(tbl2.id)
from TableName tbl2
where tbl2.companyId = tbl1.companyId)
and tbl1.active = 2
Run Code Online (Sandbox Code Playgroud)
请注意,两个表是相同的。
我把它改写为:
select count(*)
from TableName tbl1
inner join (
select max(id) as id, companyId
from TableName tbl2
where tbl2.active= 2
group by companyId
) tbl2
on tbl2.companyId = tbl1.companyId
and tbl2.id=tbl1.id
Run Code Online (Sandbox Code Playgroud)
但记录数不同。
谁能说我如何重写它以获得更好的性能?
添加索引:
CREATE INDEX index_name
ON TableName
(companyid DESC, id DESC)
INCLUDE
(active);
Run Code Online (Sandbox Code Playgroud)
试试这个重写:
SELECT COUNT_BIG(*)
FROM
(
SELECT TOP (9223372036854775807)
TN1.active
FROM TableName AS TN1
WHERE
TN1.id =
(
SELECT MAX(TN2.id)
FROM TableName AS TN2
WHERE
TN2.companyid = TN1.companyid
)
) AS SQ1
WHERE
SQ1.active = 2;
Run Code Online (Sandbox Code Playgroud)
原始查询规范似乎是:
表中有多少行active = 2;和所述ID列等于最高ID值在所有的行相同的内companyid(无活性= 2限制,为了清楚)。
有一种方法可以通过对合适索引的单次扫描来获得这些结果。
例如,给定表:
CREATE TABLE #TableName
(
companyid integer NOT NULL,
id integer NOT NULL,
active integer NOT NULL
);
Run Code Online (Sandbox Code Playgroud)
几行样本数据:
INSERT #TableName
(companyid, id, active)
VALUES
(1, 1, 2),
(1, 2, 2),
(1, 3, 0),
(2, 2, 2),
(2, 3, 2),
(2, 3, 0);
Run Code Online (Sandbox Code Playgroud)
随着索引:
CREATE INDEX index_name
ON #TableName
(companyid DESC, id DESC)
INCLUDE
(active);
Run Code Online (Sandbox Code Playgroud)
查找没有谓词(active = 2)的行的查询(基于原始)是:
SELECT
TN1.active
FROM #TableName AS TN1
WHERE
TN1.id =
(
SELECT MAX(TN2.id)
FROM #TableName AS TN2
WHERE
TN2.companyid = TN1.companyid
);
Run Code Online (Sandbox Code Playgroud)
尽管查询两次引用同一个表,但该查询的执行计划具有对索引的单次扫描:
基本思想是按键顺序扫描索引,返回每组companyid值中具有最高id的行。
不幸的是,我们不能简单地将谓词active = 2添加到该查询而不破坏为我们提供单次扫描的优化器转换。问题(如果您感兴趣的话)是新谓词被向下推到计划树,导致形状不再与“按顶部分组”变换匹配。
一种解决方法TOP是在我们想要转换的查询部分周围添加一个无意义的规范,防止新谓词被推送过去:
SELECT COUNT_BIG(*)
FROM
(
SELECT TOP (9223372036854775807)
TN1.active
FROM #TableName AS TN1
WHERE
TN1.id =
(
SELECT MAX(TN2.id)
FROM #TableName AS TN2
WHERE
TN2.companyid = TN1.companyid
)
) AS SQ1
WHERE
SQ1.active = 2;
Run Code Online (Sandbox Code Playgroud)
执行计划与之前类似,有一个额外的 Top(我们添加的无意义的 bigint.max ),一个用于active = 2谓词的过滤器,以及一个用于计算行数的最终 Stream Aggregate:
我不能保证你会用真实数据得到这个计划,或者它会更快。这完全取决于行的数量和分布。
新的 Top 还具有不赞成并行计划的效果(group by top 是并行兼容的,但全局 top 不是;并行必须停止并围绕它重新启动)。
然而,在出现更好的选择或提供更多细节之前,您可以尝试一下。
如果我正确理解您的查询,以下查询应该产生相同的结果,但如果您在 上创建索引,它可能会更有效(companyId, id)。列应按此顺序包含在索引中。
select count(*)
from TableName tbl1
where
tbl1.id =
(
select TOP(1) tbl2.id
from TableName tbl2
where tbl2.companyId = tbl1.companyId
order by tbl2.id DESC
)
and tbl1.active = 2
;
Run Code Online (Sandbox Code Playgroud)