У мене є такий запит:
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 виконується повне сканування, тобто я отримав 553 сканування повної таблиці tblFEStatsPaperHits.
Переписання лише на те, де існують, не змінює план:
DELETE FROM tblFEStatsBrowsers WHERE NOT EXISTS (
SELECT * FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID = tblFEStatsBrowsers.BrowserID
)
Однак, як запропонував Адам Маханіч, додавання параметра HASH JOIN приводить до оптимального плану виконання (лише одне сканування tblFEStatsPaperHits):
DELETE FROM tblFEStatsBrowsers WHERE NOT EXISTS (
SELECT * FROM tblFEStatsPaperHits WITH (NOLOCK) WHERE BrowserID = tblFEStatsBrowsers.BrowserID
) OPTION (HASH JOIN)
Тепер це не стільки питання, як це виправити - я можу або використовувати OPTION (HASH JOIN), або створити таблицю темпів вручну. Мені більше цікаво, чому оптимізатор запитів коли-небудь буде використовувати план, який він наразі працює.
Оскільки QO не має жодної статистики в стовпці BrowserID, я здогадуюсь, що це передбачає найгірше - 50 мільйонів чітких значень, що вимагає досить великого робочого столу пам'яті / tempdb. Таким чином, найбезпечніший спосіб - виконати сканування кожного ряду в tblFEStatsBrowsers. Між стовпцями BrowserID у двох таблицях немає зовнішнього ключа, тому QO не може виводити будь-яку інформацію з tblFEStatsBrowsers.
Це, настільки просто, як це звучить, причина?
Оновлення 1
Щоб дати кілька статистичних даних: ВАРІАНТ (HASH JOIN):
208.711 логічних читання (12 сканувань)
ВАРІАНТ (LOOP JOIN, HASH GROUP):
11.008.698 логічних читань (~ сканування на BrowserID (339))
Немає варіантів:
логічне зчитування 11.008.775 (~ сканування на BrowserID (339))
Оновлення 2
Відмінні відповіді, всі ви - дякую! Важко вибрати лише одну. Хоча Мартін був першим, і Рем пропонує чудове рішення, я мушу дати його ківі, щоб розібратися в деталях :)