Медленный отчет в SQL Server по большому набору данных - PullRequest
11 голосов
/ 16 апреля 2009

Мы используем SQL Server 2005 для отслеживания достаточного количества постоянно поступающих данных (5-15 обновлений в секунду). Мы заметили, что после того, как он работал в течение нескольких месяцев, одной из таблиц потребовалось неприличное количество времени для запроса.

Таблица имеет 3 столбца:

  • id - автономный номер (кластерный)
  • typeUUID - GUID, сгенерированный до вставки; используется для группировки типов
  • typeName - Имя типа (дух ...)

Один из запросов, которые мы выполняем, отличается от поля typeName:

SELECT DISTINCT [typeName] FROM [types] WITH (nolock);

Поле typeName содержит некластеризованный, неуникальный возрастающий индекс. В настоящий момент таблица содержит около 200 миллионов записей. Когда мы запустили этот запрос, запрос занял 5 м 58 с, чтобы вернуться! Возможно, мы не понимаем, как работают индексы ... Но я не думаю, что мы неправильно поняли их , что очень.

Чтобы проверить это немного дальше, мы запустили следующий запрос:

SELECT DISTINCT [typeName] FROM (SELECT TOP 1000000 [typeName] FROM [types] WITH (nolock)) AS [subtbl]

Этот запрос возвращается примерно через 10 секунд, как и следовало ожидать, он сканирует таблицу.

Есть что-то, что мы здесь упускаем? Почему первый запрос занимает столько времени?

Редактировать: Ах, мои извинения, первый запрос возвращает 76 записей, спасибо ninesided.

Продолжение: Спасибо всем за ваши ответы, теперь это имеет больше смысла для меня (я не знаю, почему раньше не было ...) Без индекса выполняется сканирование таблицы по 200 миллионам строк, по индексу выполняется сканирование индекса по 200 миллионам строк ...

SQL Server предпочитает индекс, и он немного повышает производительность, но не о чем беспокоиться. Восстановление индекса сократило время запроса до 3 м вместо 6 м, что является улучшением, но этого недостаточно. Я просто порекомендую своему боссу, чтобы мы нормализовали структуру таблицы.

Еще раз спасибо всем за помощь !!

Ответы [ 10 ]

9 голосов
/ 16 апреля 2009

Вы неправильно понимаете индекс. Даже если бы он использовал индекс, он все равно сделал бы сканирование индекса по 200M записей. Это займет много времени, плюс время, необходимое для выполнения DISTINCT (вызывает сортировку), и запускать это плохо Видение DISTINCT в запросе всегда поднимает красный флаг и заставляет меня дважды проверить запрос. В этом случае, возможно, у вас есть проблема нормализации?

4 голосов
/ 16 апреля 2009

Я сомневаюсь, что SQL Server даже попытается использовать индекс, ему пришлось бы выполнять практически одинаковый объем работы (учитывая узкую таблицу), читая все 200M строк независимо от того, смотрит ли он на таблицу или индекс. Если индекс на typeName был кластеризован, это может сократить время, необходимое для сортировки перед группировкой.

Если количество элементов ваших типов невелико, как насчет ведения сводной таблицы, содержащей список различных значений type? Триггер при вставке / обновлении основной таблицы будет выполнять проверку сводной таблицы и вставлять новую запись при обнаружении нового типа.

3 голосов
/ 14 сентября 2012

Существует проблема с оптимизатором SQL Server при использовании ключевого слова DISTINCT. Решением было заставить его придерживаться одного и того же плана запроса, выделив отдельный запрос отдельно.

Таким образом, мы тоже запросы, такие как:

SELECT DISTINCT [typeName] FROM [types] WITH (nolock);

и разбить его на следующее

SELECT typeName INTO #tempTable1 FROM types WITH (NOLOCK)
SELECT DISTINCT typeName FROM #tempTable1

Еще один способ обойти это - использовать GROUP BY, который получает другой план оптимизации.

1 голос
/ 26 марта 2015

Циклический подход должен использовать множественные поиски (но теряет некоторый параллелизм). Возможно, стоит попробовать для случаев с относительно небольшим количеством различных значений по сравнению с общим числом строк (низкая мощность).

Идея была из этого вопроса :

select typeName into #Result from Types where 1=0;

declare @t varchar(100) = (select min(typeName) from Types);
while @t is not null
begin
    set @t = (select top 1 typeName from Types where typeName > @t order by typeName);    
    if (@t is not null)
        insert into #Result values (@t);
end

select * from #Result;

И похоже, что есть и другие методы (особенно рекурсивный CTE @Paul White):

различные Автострада-на-найти-отчетливый-значения-быстрее методы

sqlservercentral Topic873124-338-5

1 голос
/ 16 апреля 2009

Как уже отмечали другие - когда вы делаете SELECT DISTINCT (typename) над своей таблицей, вы получите полный просмотр таблицы, несмотря ни на что.

Так что это действительно вопрос ограничения количества строк, которые должны быть отсканированы.

Вопрос в том, для чего вам нужны ваши DISTINCT typenames? И сколько из ваших 200M строк различны? У вас есть только несколько (не более нескольких сотен) разных названий ??

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

Таким образом, у вас будет отдельная небольшая таблица с отдельными записями TypeName, которая будет молниеносно запрашивать и / или отображать.

Марк

1 голос
/ 16 апреля 2009

Моя первая мысль - статистика. Чтобы найти последнее обновление:

SELECT
    name AS index_name, 
    STATS_DATE(object_id, index_id) AS statistics_update_date
FROM
    sys.indexes 
WHERE
    object_id = OBJECT_ID('MyTable');

Редактировать: Статистика обновляется при перестроении индексов, которые, как я вижу, не поддерживаются

Моя вторая мысль: индекс еще там? В запросе TOP по-прежнему должен использоваться индекс. Я только что проверил на одной из моих таблиц с 57 миллионами строк, и обе используют индекс.

0 голосов
/ 03 ноября 2017

Индексированное представление может сделать это быстрее.

create view alltypes
with schemabinding as
select typename, count_big(*) as kount
from dbo.types
group by typename

create unique clustered index idx
on alltypes (typename)

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

В качестве альтернативы вы можете создать небольшую таблицу со всеми значениями:

select distinct typename
into alltypes
from types

alter table alltypes
add primary key (typename)

alter table types add foreign key (typename) references alltypes

Внешний ключ обеспечит отображение всех используемых значений в родительской таблице alltypes. Проблема в том, что alltypes содержит , а не содержит значения, не используемые в дочерней таблице types.

0 голосов
/ 25 сентября 2015

Я мог бы что-то упустить, но было бы более эффективно, если бы при загрузке создавалось представление с различными значениями и запрашивалось бы вместо этого?

Это дало бы почти мгновенные ответы на выбор, если набор результатов значительно меньше с накладными расходами, заполняющими его при каждой записи, хотя с учетом характера представления, которое само по себе может быть тривиальным.

Он задает вопрос, сколько записей по сравнению с тем, как часто вы хотите различить и важность скорости, когда вы делаете.

0 голосов
/ 16 апреля 2009

Индекс поможет вам быстро найти строку. Но вы просите базу данных перечислить все уникальные типы для всей таблицы. Индекс не может помочь с этим.

Вы можете запустить ночное задание, которое выполняет запрос и сохраняет его в другой таблице. Если вам требуются последние данные, вы можете сохранить последний идентификатор, включенный в ночное сканирование, и объединить результаты:

select type
from nightlyscan
union
select distinct type
from verybigtable
where rowid > lastscannedid

Другой вариант - нормализовать большую таблицу на две таблицы:

talbe1: id, guid, typeid
type table: typeid, typename

Это было бы очень полезно, если бы количество типов было относительно небольшим.

0 голосов
/ 16 апреля 2009

Я должен попробовать что-то вроде этого:

SELECT typeName FROM [types] WITH (nolock)
group by typeName;

И, как и другие, я бы сказал, что вам нужно нормализовать этот столбец.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...