查询优化 - 为什么这会加快查询速度?
我在 SQL Server 2000 服务器上使用 Quest 的 TOAD for SQL Server。
这是我的查询:
SELECT CASE SLCE.GroupName WHEN 'Other' THEN ARM.FBCOMPANY
WHEN 'Inter Co.' THEN ARM.FBCOMPANY
ELSE SLCE.GroupName END AS [Company Name],
ARM.fcustno AS [Cust No],
ARM.fbcompany AS [Cust Name],
ARM.fcinvoice AS [Invoice No],
ARM.fdgldate AS [Post Date],
year(arm.fdgldate) AS [Year Posted],
CASE ARM.fcsource WHEN 'S' THEN 'Shipper'
WHEN 'O' THEN 'Sales Order'
WHEN 'R' THEN 'Receiver'
WHEN 'C' THEN 'Customer'
ELSE ARM.fcsource END AS [Source Doc Type],
CASE ARM.fcstatus WHEN 'N' THEN 'New'
WHEN 'U' THEN 'Unpaid'
WHEN 'P' THEN 'Partially Paid'
WHEN 'F' THEN 'Paid in Full'
WHEN 'H' THEN 'Held'
WHEN 'V' THEN 'Voided'
ELSE ARM.fcstatus END AS [Invoice Status],
ARM.fpono AS [Cust PO No],
ARM.fsalespn AS [Sales Person],
ARI.fitem AS [Item No],
ARI.fprodcl AS [Prod Class],
ARI.fshipkey AS [Qty Invoiced],
ARI.ftotprice AS [Net Invoiced],
ARI.fpartno AS [Part No],
ARI.frev AS [Part Rev],
cast(ARI.fmdescript AS VARCHAR(20)) AS [Part Description],
ARM.fsono AS [Sales No],
ARI.fsokey AS [SO Rels Key],
ARI.fordqty AS [Qty Ordered],
RED.[YEAR] AS [Year],
RED.PERIOD AS [RF Period]
FROM dbo.armast ARM
INNER JOIN dbo.aritem ARI
ON ARM.FCINVOICE = ARI.FCINVOICE
INNER JOIN slcdpm SLC
ON SLC.fcustno = ARM.fcustno
LEFT OUTER JOIN slcdpm_ext SLCE
ON SLC.identity_column = SLCE.fkey_id
INNER JOIN REDFRIDAYDATES..TBLREDFRIDAYALLDATES RED
ON RED.date = CAST (FLOOR (CAST (ARM.fdgldate AS FLOAT)) AS DATETIME)
WHERE ARM.fcstatus <> 'V'
AND RED.[YEAR] = year(getdate())
AND ari.frev = 'REP'
AND ARI.fsalesacc IN ('4010001', '4010002', '4010003', '4010004', '4010005', '4010006', '4010007', '4010008', '4010009', '4010010', '4010018', '4010019', '4010020', '4010021', '4010031', '4010050', '4022000', '4031000', '4045000', '4055000', '4057000', '4060000', '4070000')
这是 TOAD 的选项(突出显示差异)是:
INNER JOIN dbo.aritem ARI
ON ARM.FCINVOICE = ***COALESCE (ARI.FCINVOICE , ARI.FCINVOICE)***
INNER JOIN slcdpm SLC
ON SLC.fcustno = ARM.fcustno
LEFT OUTER JOIN slcdpm_ext SLCE
ON SLC.identity_column = SLCE.fkey_id
INNER JOIN REDFRIDAYDATES..TBLREDFRIDAYALLDATES RED
ON RED.date = CAST (FLOOR (CAST (ARM.fdgldate AS FLOAT)) AS DATETIME)
WHERE ARM.fcstatus <> 'V'
AND RED.[YEAR] = year(getdate())
AND ari.frev = 'REP'
AND ARI.fsalesacc IN ('4010001', '4010002', '4010003', '4010004', '4010005', '4010006', '4010007', '4010008', '4010009', '4010010', '4010018', '4010019', '4010020', '4010021', '4010031', '4010050', '4022000', '4031000', '4045000', '4055000', '4057000', '4060000', '4070000')
***AND ARI.[fpartno] >= CHAR(0)***
有人可以告诉我为什么合并和附加语句可以使该查询速度提高 50% 以上吗?
I'm using Quest's TOAD for SQL Server on a SQL Server 2000 Server.
Here is my query:
SELECT CASE SLCE.GroupName WHEN 'Other' THEN ARM.FBCOMPANY
WHEN 'Inter Co.' THEN ARM.FBCOMPANY
ELSE SLCE.GroupName END AS [Company Name],
ARM.fcustno AS [Cust No],
ARM.fbcompany AS [Cust Name],
ARM.fcinvoice AS [Invoice No],
ARM.fdgldate AS [Post Date],
year(arm.fdgldate) AS [Year Posted],
CASE ARM.fcsource WHEN 'S' THEN 'Shipper'
WHEN 'O' THEN 'Sales Order'
WHEN 'R' THEN 'Receiver'
WHEN 'C' THEN 'Customer'
ELSE ARM.fcsource END AS [Source Doc Type],
CASE ARM.fcstatus WHEN 'N' THEN 'New'
WHEN 'U' THEN 'Unpaid'
WHEN 'P' THEN 'Partially Paid'
WHEN 'F' THEN 'Paid in Full'
WHEN 'H' THEN 'Held'
WHEN 'V' THEN 'Voided'
ELSE ARM.fcstatus END AS [Invoice Status],
ARM.fpono AS [Cust PO No],
ARM.fsalespn AS [Sales Person],
ARI.fitem AS [Item No],
ARI.fprodcl AS [Prod Class],
ARI.fshipkey AS [Qty Invoiced],
ARI.ftotprice AS [Net Invoiced],
ARI.fpartno AS [Part No],
ARI.frev AS [Part Rev],
cast(ARI.fmdescript AS VARCHAR(20)) AS [Part Description],
ARM.fsono AS [Sales No],
ARI.fsokey AS [SO Rels Key],
ARI.fordqty AS [Qty Ordered],
RED.[YEAR] AS [Year],
RED.PERIOD AS [RF Period]
FROM dbo.armast ARM
INNER JOIN dbo.aritem ARI
ON ARM.FCINVOICE = ARI.FCINVOICE
INNER JOIN slcdpm SLC
ON SLC.fcustno = ARM.fcustno
LEFT OUTER JOIN slcdpm_ext SLCE
ON SLC.identity_column = SLCE.fkey_id
INNER JOIN REDFRIDAYDATES..TBLREDFRIDAYALLDATES RED
ON RED.date = CAST (FLOOR (CAST (ARM.fdgldate AS FLOAT)) AS DATETIME)
WHERE ARM.fcstatus <> 'V'
AND RED.[YEAR] = year(getdate())
AND ari.frev = 'REP'
AND ARI.fsalesacc IN ('4010001', '4010002', '4010003', '4010004', '4010005', '4010006', '4010007', '4010008', '4010009', '4010010', '4010018', '4010019', '4010020', '4010021', '4010031', '4010050', '4022000', '4031000', '4045000', '4055000', '4057000', '4060000', '4070000')
Here is TOAD's option (with differences highlighted) is:
INNER JOIN dbo.aritem ARI
ON ARM.FCINVOICE = ***COALESCE (ARI.FCINVOICE , ARI.FCINVOICE)***
INNER JOIN slcdpm SLC
ON SLC.fcustno = ARM.fcustno
LEFT OUTER JOIN slcdpm_ext SLCE
ON SLC.identity_column = SLCE.fkey_id
INNER JOIN REDFRIDAYDATES..TBLREDFRIDAYALLDATES RED
ON RED.date = CAST (FLOOR (CAST (ARM.fdgldate AS FLOAT)) AS DATETIME)
WHERE ARM.fcstatus <> 'V'
AND RED.[YEAR] = year(getdate())
AND ari.frev = 'REP'
AND ARI.fsalesacc IN ('4010001', '4010002', '4010003', '4010004', '4010005', '4010006', '4010007', '4010008', '4010009', '4010010', '4010018', '4010019', '4010020', '4010021', '4010031', '4010050', '4022000', '4031000', '4045000', '4055000', '4057000', '4060000', '4070000')
***AND ARI.[fpartno] >= CHAR(0)***
Can someone please tell me why that coalesce and additional and statement speed up this query by more than 50%?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您看过实际执行计划吗?这些应该向您展示 SQL Server 在执行这些查询时采用的不同方法。
Have you taken a look at the Actual Execution Plans. These should show you the different approaches that SQL Server took in executing these queries.
这绝对是一件奇怪的事。执行计划应该可以肯定地告诉您,但是数据库中这样的性能变化几乎总是归结为索引。所以我最好的猜测是,不知何故,sql server 缺少它可以使用的索引,并且添加这些奇怪的更改使其更加突出。
但是,如果您想了解其中涉及的“原因”,以便下次可以更快地编写查询,那么实际上什么也没有。
It's definitely an odd one. The execution plan should tell you for sure, but performance changes like this in databases almost always come down to an index. So my best guess is that somehow sql server was missing an index it could use and adding these odd changes made it stand out better.
But if you're looking to learn the 'why' involved so that next time you can write your query to be faster in the first place, there's really nothing there.
完整的 WAG:
我猜测 fpartno 上有一个“非空”条件(因此添加的过滤器总是通过),并且 Toad 碰巧知道 SQLServer 不够聪明,无法检测到 >=CHAR(0 ) 始终为真。因此,这表明 Toad 试图以一种非常间接的方式引导优化器使用其中包含 fpartno 的内容。那东西可能是 (fcinvoice, fpartno) 上的综合索引..你有其中之一吗?
正如其他人所说,解释计划应该有助于解释这个谜团。
A complete WAG:
I am going to guess that fpartno has a "not null" condition on it (so the added filter always passes), and that Toad happens to know that SQLServer is not smart enough to detect that >=CHAR(0) is always true. So this suggests that Toad is trying to guide, in a very oblique way, the optimizer to use something that has fpartno in it. That something could be a composite index on (fcinvoice, fpartno).. do you have one of those?
Like the others said, the explain plan should prove helpful in explaining the mystery.
这些更改对查询的逻辑含义都没有任何影响(即,它们是“无操作”)。
ON 子句上的 COALESCE 的唯一物理影响是阻止优化器尝试对 ARI 使用索引。 FCINVOICE。
同样,“>= CHAR(0)”对优化器的唯一物理影响可能是促使其考虑对索引使用索引范围扫描(或查找)其中有 ARI.[fpartno] 。
所以我的结论是,TOAD 试图操纵优化器使用特定索引,而实际上并没有强制它通过显式提示使用该索引。实际上,这有点聪明,因为 T-SQL 优化器提示的真正问题是它们的依赖性和脆弱性。
Neither of these changes have any effect on the logical meaning of the query (i.e., they are "no ops")
The only physical effect that the COALESCE on the ON clause would be to prevent the optimizer from trying to use an index for ARI.FCINVOICE.
Likewise, the only physical effect that the ">= CHAR(0)" could have on the optimizer might be to prod it to consider using a indexed range scan (or also a seek) on an index that had ARI.[fpartno] in it.
So my conclusion would be that TOAD is trying to manipulate the optimizer into using a particular index without actually forcing it to use that index with an explicit HINT. Actually, that's kind of clever as the real problem with the T-SQL optimizer hints is their dependencies and fragility.