Поскольку мои настройки исполнительское мастерство никогда не кажется, чувствуют себя достаточно, я всегда задаюсь вопросом, если есть более оптимизация я могу выполнять против некоторых запросов. Ситуация, к которой относится этот вопрос, - это функция Windowed MAX, вложенная в подзапрос.
Данные, которые я копаю, представляют собой серию транзакций для различных групп больших наборов. У меня есть 4 поля важности, уникальный идентификатор транзакции, идентификатор группы пакета транзакций и даты, связанные с соответствующей уникальной транзакцией или группой транзакций. В большинстве случаев Дата группы совпадает с Максимальной уникальной датой транзакции для партии, но бывают случаи, когда ручные корректировки проходят через нашу систему, и после захвата даты групповой транзакции происходит операция с уникальной датой. Это ручное редактирование не корректирует дату группы по дизайну.
В этом запросе я идентифицирую те записи, в которых уникальная дата находится после даты группы. В следующем примере запроса создается грубый эквивалент сценария my, и оператор SELECT возвращает искомые записи, однако, подхожу ли я к этому решению наиболее эффективным способом? Это требует времени для запуска во время загрузки моей таблицы фактов, так как моя запись считает число в верхних 9 цифрах, но в основном мое пренебрежение к подзапросам заставляет меня задуматься, есть ли лучший подход здесь. Я не настолько обеспокоен какими-либо показателями, как уверен, что они уже есть; что я ищу, так это альтернативный подход к запросу, который позволит добиться того же, но еще более эффективно. Любые отзывы приветствуются.
CREATE TABLE #Example
(
UniqueID INT IDENTITY(1,1)
, GroupID INT
, GroupDate DATETIME
, UniqueDate DATETIME
)
CREATE CLUSTERED INDEX [CX_1] ON [#Example]
(
[UniqueID] ASC
)
SET NOCOUNT ON
--Populate some test data
DECLARE @i INT = 0, @j INT = 5, @UniqueDate DATETIME, @GroupDate DATETIME
WHILE @i < 10000
BEGIN
IF((@i + @j)%173 = 0)
BEGIN
SET @UniqueDate = GETDATE()+@i+5
END
ELSE
BEGIN
SET @UniqueDate = GETDATE()+@i
END
SET @GroupDate = GETDATE()+(@j-1)
INSERT INTO #Example (GroupID, GroupDate, UniqueDate)
VALUES (@j, @GroupDate, @UniqueDate)
SET @i = @i + 1
IF (@i % 5 = 0)
BEGIN
SET @j = @j+5
END
END
SET NOCOUNT OFF
CREATE NONCLUSTERED INDEX [IX_2_4_3] ON [#Example]
(
[GroupID] ASC,
[UniqueDate] ASC,
[GroupDate] ASC
)
INCLUDE ([UniqueID])
-- Identify any UniqueDates that are greater than the GroupDate within their GroupID
SELECT UniqueID
, GroupID
, GroupDate
, UniqueDate
FROM (
SELECT UniqueID
, GroupID
, GroupDate
, UniqueDate
, MAX(UniqueDate) OVER (PARTITION BY GroupID) AS maxUniqueDate
FROM #Example
) calc_maxUD
WHERE maxUniqueDate > GroupDate
AND maxUniqueDate = UniqueDate
DROP TABLE #Example
dbfiddle здесь
источник
Ответы:
Я предполагаю, что нет никакого индекса, поскольку Вы не предоставили никакого.
Сразу после этого следующий индекс исключит в вашем плане оператора сортировки, который в противном случае мог бы потреблять много памяти:
В этом случае подзапрос не является проблемой производительности. Во всяком случае, я бы искал способы исключить оконную функцию (MAX ... OVER), чтобы избежать конструкции Nested Loop и Table Spool.
С тем же индексом следующий запрос может на первый взгляд выглядеть менее эффективным, и он выполняет от двух до трех проверок в базовой таблице, но он устраняет огромное количество операций внутреннего чтения, поскольку в нем отсутствуют операторы Spool. Я предполагаю, что он все равно будет работать лучше, особенно если у вас достаточно процессорных ядер и производительности ввода-вывода на вашем сервере:
(Примечание: я добавил
MERGE JOIN
подсказку к запросу, но это, вероятно, должно произойти автоматически, если ваша статистика в порядке. Рекомендуется оставлять подобные подсказки, если вы можете.)источник
Когда и если вам удастся выполнить обновление с SQL Server 2012 до SQL Server 2016, вы сможете воспользоваться значительно улучшенной производительностью (особенно для бескаркасных оконных агрегатов), предоставляемой новым оператором агрегации окон в пакетном режиме.
Почти все большие сценарии обработки данных работают лучше с хранилищем columnstore, чем rowstore. Даже не переходя на columnstore для базовых таблиц, вы все равно можете воспользоваться преимуществами нового оператора 2016 и выполнения в пакетном режиме, создав пустой некластеризованный отфильтрованный индекс columnstore для одной из базовых таблиц или избыточно подключив его к организованному columnstore. Таблица.
Используя второй вариант, запрос становится:
дб <> скрипка
Обратите внимание, что единственным изменением исходного запроса является создание пустой временной таблицы и добавление левого соединения. План выполнения:
Для получения дополнительной информации и опций см. Превосходную серию Ицик Бен-Гана « Что нужно знать об операторе агрегирования окна пакетного режима в SQL Server 2016» (из трех частей).
источник
Я просто брошу Крест, подай заявку там:
С какими-то какими-то индексами это получается довольно хорошо.
Статистика и время выглядят так (ваш запрос - первый результат)
Планы запросов здесь (опять же, ваш первый):
https://www.brentozar.com/pastetheplan/?id=BJYJvqAal
Почему я предпочитаю эту версию? Я избегаю катушек. Если те начнут выливаться на диск, это будет ужасно.
Но вы можете попробовать это тоже.
Если это большой DW, вы можете предпочесть хеш-соединение и фильтрацию строк в соединении, а не в конце
TOP 1
запроса в качестве оператора фильтра.План здесь: https://www.brentozar.com/pastetheplan/?id=BkUF55ATx
Время и статистика здесь:
Надеюсь это поможет!
Одно редактирование, основанное на идее @ ypercube, и новый индекс.
Вот статистика времени и IO:
Вот план:
https://www.brentozar.com/pastetheplan/?id=SJv8foR6g
источник
Я бы посмотрел на
top with ties
Если
GroupDate
то же самое вGroupId
то время:Остальное: использование
top with ties
в общем табличном выраженииdbfiddle: http://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=c058994c2f5f3d99b212f06e1dae9fd3
Оригинальный запрос
против
top with ties
в общем табличном выраженииисточник
Итак, я провел некоторый анализ различных подходов, опубликованных до сих пор, и в моем окружении похоже, что подход Дэниела последовательно выигрывает по времени выполнения. Удивительно (для меня) третий подход sp_BlitzErik CROSS APPLY не так уж далеко позади. Вот результаты, если кому-то интересно, но спасибо ТОНА за все альтернативные подходы. Изучив ответы на этот вопрос, я узнал больше, чем за довольно долгое время!
источник
top with ties
пряжек с таким количеством строк. dbfiddle.uk/...