以下查询需要永远完成。但如果我删除前10名条款,它会很快完成。 big_table_1和big_table_2是2个表,有10 ^ 5条记录。
我曾经认为顶级条款会降低时间成本,但显然不在这里。为什么???
select top 10 ServiceRequestID
from
(
(select *
from big_table_1
where big_table_1.StatusId=2
) cap1
inner join
big_table_2 cap2
on cap1.ServiceRequestID = cap2.CustomerReferenceNumber
)
答案 0 :(得分:13)
关于同一主题还有其他stackoverflow讨论(底部的链接)。正如上面的评论中所指出的,它可能与索引和优化器混淆并使用错误的索引有关。
我的第一个想法是你正在从(select * ....)执行select top serviceid,并且优化器可能难以将查询推送到内部查询并使用索引。
考虑将其重写为
select top 10 ServiceRequestID
from big_table_1
inner join big_table_2 cap2
on cap1.servicerequestid = cap2.customerreferencenumber
and big_table_1.statusid = 2
在您的查询中,数据库可能正在尝试合并结果并返回它们,然后将其限制为外部查询中的前10位。在上面的查询中,数据库只需要在合并结果时收集前10个结果,从而节省了大量时间。如果servicerequestID被索引,它肯定会使用它。在您的示例中,查询正在查找已经以虚拟的无索引格式返回的结果集中的servicerequestid列。
希望这是有道理的。虽然假设优化器应该采用我们放入SQL的任何格式并找出每次返回值的最佳方法,但事实是我们将SQL放在一起的方式可以真正影响在某些步骤中执行某些步骤的顺序。 DB。
SELECT TOP is slow, regardless of ORDER BY
Why is doing a top(1) on an indexed column in SQL Server slow?
答案 1 :(得分:4)
这也取决于“完成”的含义。如果“完成”意味着你开始在gui上看到一些显示,这并不一定意味着查询已经完成执行。这可能意味着结果开始流入,而不是流式传输完成。当您将其包装到子查询中时,外部查询无法在内部查询的所有结果都可用之前进行处理:
在Oracle中,有“first_rows”和“all_rows”提示与操纵此类行为有些相关。 AskTom discussion。
如果内部查询在生成第一行和生成最后一行之间需要很长时间,那么这可能是发生了什么的指示。作为调查的一部分,我将采用内部查询并修改它以具有分组功能(或排序)以强制处理所有行,然后才能返回结果。我会用它来衡量内部查询与外部查询中的时间进行比较所需的时间。
稍微偏离主题,尝试在Oracle中模拟这样的事情可能会很有趣:创建一个流水线函数来流回数字;流回一些(比如15),然后旋转一段时间再流回来。
使用jdbc客户端对流水线函数执行executeQuery。默认情况下,Oracle语句fetchSize为10。使用时间戳循环并打印结果。看看结果是否错开。我无法使用Postgresql(RETURN NEXT)对此进行测试,因为Postgres不会从函数中传输结果。
流水线表函数立即向其调用者返回一行 在处理该行并继续处理行之后。响应时间 因为不需要构建整个集合而得到改进 在查询返回单个结果之前返回到服务器 行。 (此外,该功能需要更少的内存,因为对象缓存 不需要实现整个集合。)
注意:RETURN NEXT和RETURN QUERY的当前实现 在从函数返回之前存储整个结果集,如 上面讨论过。这意味着如果PL / pgSQL函数产生一个 非常大的结果集,性能可能很差:数据将被写入 到磁盘以避免内存耗尽,但功能本身不会 返回,直到生成整个结果集。未来 PL / pgSQL版本可能允许用户定义set-returns 没有此限制的功能。
statement.setFetchSize(100);
答案 2 :(得分:3)
我无法解释原因,但我可以提出一个想法:
尝试在查询之前添加SET ROWCOUNT 10
。在某些情况下它帮助了我。请记住,这是一个范围设置,因此您必须在运行查询后将其设置回原始值。
说明: SET ROWCOUNT:导致SQL Server在返回指定行数后停止处理查询。
答案 3 :(得分:2)
在调试这样的事情时,我发现找出SQL Server“看到”两个查询的最快方法是查看他们的查询计划。在查询视图中点击SSMS中的CTRL-L
,结果将显示在实际执行查询时用于构建结果的逻辑。
SQL Server维护有关表格数据的统计信息,例如具有特定范围内数据的行数的直方图。它收集并使用这些统计信息来尝试预测对这些表运行查询的“最佳”方式。例如,它可能有数据表明某些输入可能需要返回1M行的特定子查询,而对于其他输入,相同的子查询可能返回1000行。这可以使它选择不同的策略来构建结果,例如使用表扫描(穷举搜索表)而不是索引搜索(向右跳到所需数据)。如果统计数据不能充分代表数据,则可以选择“错误”策略,结果与您遇到的结果类似。我不知道这是不是问题,但这就是我要找的东西。
答案 4 :(得分:2)
我的查询与您的查询类似。订购的查询但没有顶部子句需要1秒,与顶部3相同的查询需要1分钟。
我看到使用变量顶部按预期工作。
您案件的代码:
declare @top int = 10;
select top (@top) ServiceRequestID
from
(
(select *
from big_table_1
where big_table_1.StatusId=2
) cap1
inner join
big_table_2 cap2
on cap1.ServiceRequestID = cap2.CustomerReferenceNumber
)
答案 5 :(得分:1)
除非您使用order by,否则TOP不会根据我的知识对结果进行排序。
所以我的猜测是,正如有人已经建议的那样,查询不会花费更长的时间来执行。当您在查询中没有TOP时,您只需开始更快地看到结果。
尝试使用@sql_mommy查询,但请确保您拥有以下内容:
为了让您的查询更快地运行,您可以在big_table_1中创建servicerequestid和statusid的索引,在big_table_2中创建customerreferencenumber的索引。如果您创建非聚簇索引,则应该获得具有非常快速结果的仅索引计划。
如果我没记错的话,TOP结果将与你在big_table_1上的索引的顺序相同,但我不确定。
吉斯利
答案 6 :(得分:1)
如果要比较两个查询的性能,则必须在相同的情况下运行这两个查询(使用干净的内存缓冲区)并具有mumeric统计信息
为每个查询运行此批处理以比较执行时间和统计结果 (不要在生产环境中运行):
DBCC FREEPROCCACHE
GO
CHECKPOINT
GO
DBCC DROPCLEANBUFFERS
GO
SET STATISTICS IO ON
GO
SET STATISTICS TIME ON
GO
-- your query here
GO
SET STATISTICS TIME OFF
GO
SET STATISTICS IO OFF
GO
答案 7 :(得分:1)
我只是需要调查一个非常类似的问题。
SELECT TOP 5 *
FROM t1 JOIN t2 ON t2.t1id = t1.id
WHERE t1.Code = 'MyCode'
ORDER BY t2.id DESC
t1有100K行,t2为20M行,来自t1.Code的连接表的平均行数约为35K。实际的结果集只有3行,因为t1.Code =' MyCode'只匹配2行,在t2中只有3个对应的行。统计数据是最新的。
使用上面的TOP 5,查询需要几分钟,删除TOP 5后立即返回查询。
有TOP和没有TOP的计划完全不同。
我认为我的TOP计划如此糟糕,因为从t1和t2中挑选的行是一些最新的行(t1.id和t2.id的最大值)。查询优化器假设从均匀分布的平均结果集中挑选前5行将比非TOP方法更快。我使用最早的行中的t1.code测试了这个理论,使用相同的计划,响应是亚秒级。
因此,至少在我的情况下,结论是问题是由于数据分布不均匀而未在统计数据中反映出来。
答案 8 :(得分:0)
比较两者之间的执行计划可能是个好主意。您的统计信息可能已过期。如果您发现实际执行计划之间存在差异,那么您的绩效就会有所不同。
在大多数情况下,您会期望在前10名中获得更好的表现。在您的情况下,表现会更差。如果是这种情况,您不仅会看到执行计划之间的差异,而且还会看到估计执行计划和实际执行计划中返回行数的差异,从而导致SQL引擎的不良分解
重新计算统计数据后再次尝试(当你正在使用它时,重建索引)
同时检查取出where big_table_1.StatusId=2
是否有帮助,而不是
select top 10 ServiceRequestID
from big_table_1 as cap1 INNER JOIN
big_table_2 as cap2
ON cap1.ServiceRequestID = cap2.CustomerReferenceNumber
WHERE cap1.StatusId=2
我发现这种格式更具可读性,但它应该(尽管可能远程可能不会)优化到同一个执行计划。无论
,返回的endresult都是相同的