SQL Server 2008 Оптимизируйте полное соединение с отказанными операторами isnull
-
26-09-2019 - |
Вопрос
Всем привет
Я надеялся, что кто-то может помочь мне улучшить запрос, которого я должен периодически бежать. На данный момент выполняется более 40 минут. В течение этого времени он использует полную выделенную память в течение этого времени, но использование CPU в основном мешает на 2% - 5%, время от времени, а затем прыгает на 40% в течение нескольких секунд.
У меня есть этот стол (упрощенный пример):
CREATE TABLE [dbo].[dataTable]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[dteEffectiveDate] [date] NULL,
[dtePrevious] [date] NULL,
[dteNext] [date] NULL,
[Age] [int] NULL,
[Count] [int] NULL
) ON [PRIMARY]
GO
Вот некоторые входные значения:
INSERT INTO [YourDB].[dbo].[dataTable]
([dteEffectiveDate]
,[dtePrevious]
,[dteNext]
,[Age]
,[Count])
VALUES
('2009-01-01',NULL,'2010-01-01',40,300),
('2010-01-01','2009-01-01', NULL,40,200),
('2009-01-01',NULL, '2010-01-01',20,100),
('2010-01-01','2009-01-01', NULL,20,50),
('2009-01-01',NULL,'2010-01-01',30,10)
GO
Каждая запись имеет поле DeTeeffectiveDate. Кроме того, у каждого есть DTEPREVION и DTENEXT, который отражает даты ближайшей предыдущей / следующей даты вступления в силу. Теперь, что я хочу, это запрос, который будет рассчитать средние значения на полях подсчета между последовательными периодами, в определенном возрасте.
Таким образом, например, в данных выше, в возрасте 40 лет у нас 300 в 2009/01/01 и 200 годах в 2010/01/01, поэтому запрос должен производить 250.
Обратите внимание, что возраст 30 имеет только одну запись, 10. Это в 2009/01/01. В 2010 году нет входа в 2010/01/01, но мы знаем, что данные были захвачены на данный момент, поэтому тот факт, что нет ничего означает, что 30 - 0 на этой дате. Следовательно, запрос должен производить 5.
Для достижения этого я использую полное соединение таблицы на себе и используйте isnull для выбора значений. Вот мой код:
SELECT
ISNULL(T1.dteEffectiveDate,T2.dtePrevious) as [Start Date]
,ISNULL(T1.dteNext,T2.dteEffectiveDate) as [End Date]
,ISNULL(T1.Age,T2.Age) as Age
,ISNULL(T1.[Count],0) as [Count Start]
,ISNULL(T2.[Count],0) as [Count End]
,(ISNULL(T1.[Count],0)+ISNULL(T2.[Count],0))/2 as [Mid Count]
FROM
[ExpDBClient].[dbo].[dataTable] as T1
FULL JOIN [ExpDBClient].[dbo].[dataTable] as T2
ON
T2.dteEffectiveDate = T1.dteNext
AND T2.Age = T1.Age
WHERE ISNULL(T1.dteEffectiveDate,T2.dtePrevious) is not null
AND ISNULL(T1.dteNext,T2.dteEffectiveDate) is not null
GO
какие выходы:
Start Date End Date Age Count Start Count End Mid Lives
2009-01-01 2010-01-01 40 300 200 250
2009-01-01 2010-01-01 20 100 50 75
2009-01-01 2010-01-01 30 10 0 5
Он отлично работает, но когда я запускаю это по фактическим данным, что составляет около 7 м записей, это не очень долго выполняет.
У кого-нибудь есть предложения?
Спасибо
Карл
Решение
Трудно сделать много рекомендаций.
Одна вещь, которую я бы определенно рекомендовал, это индексы на этих столбцах, которые вы используете в качестве иностранных клавиш в ваших условиях соединения, например,
Age
dteEffectiveDate
dteNext
Создайте неуструйный индекс на каждой из этих столбцов отдельно и измерить снова. Благодаря нескольким рядам данных нет измеримых улучшений - но с миллионами строк, это может изменить ситуацию.