SQL Server 2008 Оптимизируйте полное соединение с отказанными операторами isnull

StackOverflow https://stackoverflow.com/questions/3812595

Вопрос

Всем привет

Я надеялся, что кто-то может помочь мне улучшить запрос, которого я должен периодически бежать. На данный момент выполняется более 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

Создайте неуструйный индекс на каждой из этих столбцов отдельно и измерить снова. Благодаря нескольким рядам данных нет измеримых улучшений - но с миллионами строк, это может изменить ситуацию.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top