2016-09-21 4 views
1

Я таблица большой (> 100 миллионов строк) в моей базе данных MS SQL со следующими столбцами:Форсировочная SQL запросов с агрегатами на DateTime и группы по

Id int not null, 
ObjectId int not null, 
Timestamp datetime not null 
State int not null 

Id это первичный ключ таблицы (и на нем есть кластерный индекс). Я добавил не кластеризованный индекс в Timestamp и ObjectId (в этом порядке). В ObjectId всего около 2000 различных значений. Я хочу выполнить следующие запросы:

SELECT ObjectId, MAX(Timestamp) FROM Table GROUP BY ObjectId 

Это занимает около четырех секунд, что слишком медленно для моего приложения. В плане выполнения говорится, что 97% времени выполнения относится к сканированию индексов некластеризованного индекса.

На копии таблицы я создал кластерный указатель на ObjectId и Timestamp. Результирующая среда выполнения такая же, в плане выполнения говорится, что теперь она выполняет сканирование индексов кластерного индекса.

Есть ли другая возможность улучшить время выполнения без разделения данных таблицы на несколько таблиц?

+1

Вы пробовали указатель только на ObjectID? Хотя я не ожидаю, что это улучшит дело, так как запрос, который вы выполняете, должен касаться каждой строки в базе данных в любом случае. IMO не будет улучшено, если вы не улучшаете свой сервер db или не перепроектируете свою схему (например, вы можете добавить вторую таблицу, которая сохраняет максимальную временную метку для каждого объекта с помощью триггера). – jeroenh

+0

@ jeroenh: Да, я тоже пробовал это без каких-либо замечательных результатов. –

+1

На самом деле мы можем предположить, что вы часто вставляете данные в эту таблицу, возможно, добавляете подсказку в свой запрос: WITH (NOLOCK) –

ответ

1

Я могу предложить вам другой ответ, добавить логический столбец LAST и обновить last true для ObjectID до false, прежде чем вставить строку now для этого ObjectID с LAST на true. Создайте индекс для ObjectID и LAST. Запрос очень прост:

SELECT ObjectId, Timestamp FROM Table where LAST = true 

Нет больше группового и fullscan, но еще одно обновление для вставки.

+0

Хорошая идея. Я пробовал это, и он отлично работает. Дополнительное ОБНОВЛЕНИЕ не имеет большого значения.Это быстро и для меня случай, когда время выполнения инструкции SELECT намного более важно. –

0

4 секунды для неплохих результатов для работы в БД с более 100 М строк. Вы можете ежедневно архивировать некоторые данные в другой таблице для сохранения исторических данных. Вы можете архивировать все данные в другой таблице и удалить старые изменения объектов:

delete from TABLE where Id in (select t1.Id from Table t1, Table t2 
where t1.ObjectId = t2.ObjectId and t1.Timestamp < t2.Timestamp) 
+0

Нет, это действительно плохо, 4s около 3,9 с :) И мой вопрос \t явно исключил решения, которые копируют часть данных в другие таблицы. –

+1

Итак, вы можете купить лучшую машину или точную настройку MySQL («key_buffer_size» в файле my.cnf для увеличения использования индексов с использованием ram), но вы ставите хорошие индексы. Данные в базе данных необходимо архивировать, вы не можете думать, что ваша модель может расти до бесконечности без проблем с производительностью. –

+0

Дело в том, что указанный выше запрос имеет время выполнения, которое линейно зависит от количества строк в таблице. И это субоптимально, не имеет значения, есть ли миллион или один миллиард строк в таблице. Поскольку в плане выполнения говорится, что он просматривает всю таблицу, перед началом некоторых методов архивирования есть что-то улучшить. –

0

Для этого конкретного запроса, индекс по (ObjectId, Timestamp) будет оптимальным. И есть вероятность, что (ObjectId, Timestamp DESC) будет работать еще быстрее.