我有一个类似以下的查询:
DELETE FROM tblFEStatsBrowsers WHERE BrowserID NOT IN (
SELECT DISTINCT BrowserID FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID IS NOT NULL
)
tblFEStatsBrowsers有553行。
tblFEStatsPaperHits已获得47.974.301行。
tblFEStatsBrowsers:
CREATE TABLE [dbo].[tblFEStatsBrowsers](
[BrowserID] [smallint] IDENTITY(1,1) NOT NULL,
[Browser] [varchar](50) NOT NULL,
[Name] [varchar](40) NOT NULL,
[Version] [varchar](10) NOT NULL,
CONSTRAINT [PK_tblFEStatsBrowsers] PRIMARY KEY CLUSTERED ([BrowserID] ASC)
)
tblFEStatsPaperHits:
CREATE TABLE [dbo].[tblFEStatsPaperHits](
[PaperID] [int] NOT NULL,
[Created] [smalldatetime] NOT NULL,
[IP] [binary](4) NULL,
[PlatformID] [tinyint] NULL,
[BrowserID] [smallint] NULL,
[ReferrerID] [int] NULL,
[UserLanguage] [char](2) NULL
)
tblFEStatsPaperHits上有一个聚集索引,其中不包含BrowserID。因此,执行内部查询将需要对tblFEStatsPaperHits进行全表扫描-完全可以。
当前,对tblFEStatsBrowsers中的每一行都执行了完整扫描,这意味着我已经对tblFEStatsPaperHits进行了553次全表扫描。
仅重写为“ WHERE EXISTS”不会改变计划:
DELETE FROM tblFEStatsBrowsers WHERE NOT EXISTS (
SELECT * FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID = tblFEStatsBrowsers.BrowserID
)
但是,正如Adam Machanic所建议的那样,添加HASH JOIN选项确实可以实现最佳的执行计划(只需对tblFEStatsPaperHits进行一次扫描):
DELETE FROM tblFEStatsBrowsers WHERE NOT EXISTS (
SELECT * FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID = tblFEStatsBrowsers.BrowserID
) OPTION (HASH JOIN)
现在,这不再是如何解决此问题的问题-我可以使用OPTION(哈希联接)或手动创建临时表。我更想知道为什么查询优化器会使用其当前执行的计划。
由于QO在BrowserID列上没有任何统计信息,因此我猜测它是假设最坏的值-5000万个不同的值,因此需要相当大的内存/ tempdb工作表。因此,最安全的方法是对tblFEStatsBrowsers中的每一行执行扫描。两个表中的BrowserID列之间没有外键关系,因此QO无法从tblFEStatsBrowsers中扣除任何信息。
听起来很简单,这是原因吗?
更新1
要提供一些统计信息:选项(
哈希联接):208.711逻辑读取(12次扫描)
选项(LOOP JOIN,HASH GROUP):
11.008.698逻辑读取(〜按浏览器ID扫描(339))
无选项:
11.008.775逻辑读取(〜按浏览器ID(339)进行扫描)
更新2
,所有人,很好的答案-谢谢!很难选择一个。尽管马丁是第一位,雷木思提供了一个出色的解决方案,但我不得不将其交给新西兰人,以便于在细节上投入精力:)