2016-01-06 4 views
9

Я выполнил следующий запрос по данным предыдущих лет, и потребовалось 3 часа, в этом году потребовалось 13 дней. Я не знаю, почему это так. Любая помощь приветствуется.Запрос SQL Server Update очень медленный

Я только что протестировал запросы на старом SQL-сервере и работает через 3 часа. Поэтому проблема должна иметь какое-то отношение к новому SQL-серверу, который я создал. Есть ли у вас какие-либо идеи в отношении проблемы?

Запрос:

USE [ABCJan] 
CREATE INDEX Link_Oct ON ABCJan2014 (Link_ref) 
GO 
CREATE INDEX Day_Oct ON ABCJan2014 (date_1) 
GO 

UPDATE ABCJan2014 
SET  ABCJan2014.link_id = LT.link_id 
FROM  ABCJan2014 MT 
INNER JOIN [Central].[dbo].[LookUp_ABC_20142015] LT 
ON MT.Link_ref = LT.Link_ref 

UPDATE ABCJan2014 
SET  SumAvJT = ABCJan2014.av_jt * ABCJan2014.n 

UPDATE ABCJan2014 
SET  ABCJan2014.DayType = LT2.DayType 
FROM  ABCJan2014 MT 
INNER JOIN [Central].[dbo].[ABC_20142015_days] LT2 
ON MT.date_1 = LT2.date1 

С помощью следующих структур данных:

ABCJan2014 (70 миллионов строк - НЕТ Unique Identifier - Link_ref & дата_1 вместе являются уникальными)

Link_ID nvarchar (17) 
Link_ref int 
Date_1 smalldatetime 
N  int 
Av_jt  int 
SumAvJT decimal(38,14) 
DayType nvarchar (50) 

LookUp_ABC_20142015

Link_ID nvarchar (17) PRIMARY KEY 
Link_ref int INDEXED 
Link_metres int 

ABC_20142015_days

Date1 smalldatetime PRIMARY KEY & INDEXED 
DayType nvarchar(50) 

ВЫПОЛНЕНИЕ ПЛАНА enter image description here

Это, как представляется, эта часть запроса, принимает такое долгое время.

Еще раз спасибо за любую помощь, я вытаскиваю волосы.

+1

Просьба представить план выполнения ваших 'заявлений UPDATE' – Devart

+0

Сколько записей обновляется? – User2012384

+1

У вас есть индексы на LT.Link_ref и LT2.date1? Если вы этого не сделаете, это будет проблемой. – rghome

ответ

1

Если посмотреть на план выполнения время находится в фактическом обновлении

Посмотрите на файл журнала
ли файл журнала на быстрый диск?
Является ли файл журнала на том же физическом диске?
Должен ли файл журнала расти?
Размер файла журнала нравится 1/2 размер файла данных

Что касается теста индексов и настроить этот
Если объединяемых столбцов индексируются не так много, чтобы сделать здесь

select count(*) 
FROM  ABCJan2014 MT 
INNER JOIN [Central].[dbo].[LookUp_ABC_20142015] LT 
ON MT.Link_ref = LT.Link_ref 

select count(*) 
FROM  ABCJan2014 MT 
INNER JOIN [Central].[dbo].[ABC_20142015_days] LT2 
ON MT.date_1 = LT2.date1 

Start с верхней (1000), чтобы получить настройки обновления работает
для усмешек пожалуйста, дайте это попробовать
Пожалуйста, пост этот план запроса
(НЕ добавить индекс ABCJan2014 LINK_ID)

UPDATE top (1000) ABCJan2014 
SET  MT.link_id = LT.link_id 
FROM  ABCJan2014 MT 
JOIN  [Central].[dbo].[LookUp_ABC_20142015] LT 
      ON MT.Link_ref = LT.Link_ref 
     AND MT.link_id <> LT.link_id 

Если LookUp_ABC_20142015 не активен, то добавьте NOLOCK

JOIN  [Central].[dbo].[LookUp_ABC_20142015] LT with (nolock) 

NVARCHAR (17) для ПК мне просто странно
почему п - у вас действительно есть некоторые юникод?
Почему не просто символ (17) и пусть он выделяет пространство?

0
ALTER TABLE dbo.ABCJan2014 
    ADD SumAvJT AS av_jt * n --PERSISTED 

CREATE INDEX ix ON ABCJan2014 (Link_ref) INCLUDE (link_id) 
GO 
CREATE INDEX ix ON ABCJan2014 (date_1) INCLUDE (DayType) 
GO 

UPDATE ABCJan2014 
SET ABCJan2014.link_id = LT.link_id 
FROM ABCJan2014 MT 
JOIN [Central].[dbo].[LookUp_ABC_20142015] LT ON MT.Link_ref = LT.Link_ref 

UPDATE ABCJan2014 
SET ABCJan2014.DayType = LT2.DayType 
FROM ABCJan2014 MT 
JOIN [Central].[dbo].[ABC_20142015_days] LT2 ON MT.date_1 = LT2.date1 
+0

22 часа спустя, и он все еще обновляет link_id ... Любые другие идеи? – hc91

+0

Это может быть что угодно ... Замки на целевой таблице, субоптимальный план выполнения и т. Д. Пожалуйста, предоставьте «ПОЛНЫЙ» план выполнения для операторов «UPDATE» – Devart

+0

Я отредактировал вопрос - надеюсь, это обеспечивает полный план выполнения это может помочь. Спасибо за вашу помощь. – hc91

2

Создать индекс на ABCJan2014 таблицы, как это в настоящее время куча

0

Я думаю, есть много страниц расщепления. Можете ли вы попробовать это?

SELECT 

(SELECT LT.link_id FROM [Central].[dbo].[LookUp_ABC_20142015] LT 
WHERE MT.Link_ref = LT.Link_ref) AS Link_ID, 
Link_ref, 
Date_1, 
N, 
Av_jt, 
MT.av_jt * MT.n AS SumAvJT, 
(SELECT LT2.DayType FROM [Central].[dbo].[ABC_20142015_days] LT2 
WHERE MT.date_1 = LT2.date1) AS DayType 

INTO ABCJan2014new 
FROM ABCJan2014 MT 
1

Если вы собираетесь обновлять таблицу, вам нужен уникальный идентификатор, поэтому надевайте ABCJan2014 как можно скорее, так как он такой большой. Нет причин, по которым вы не можете создать уникальный индекс в полях, которые вместе составляют уникальную запись. В будущем никогда не создавайте таблицу, которая не имеет уникального индекса или ПК. Это просто задает проблемы как во время обработки, так и, что более важно, в целостности данных.

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

UPDATE ABCJan2014 
SET ABCJan2014.link_id = LT.link_id 
FROM ABCJan2014 MT 
JOIN [Central].[dbo].[LookUp_ABC_20142015] LT ON MT.Link_ref = LT.Link_ref 

Приведенный выше код обновит все записи из соединения. Если в некоторых записях уже есть ссылка, вы можете сэкономить значительное время, только обновив записи, где link_id имеет значение null или ABCJan2014.link_id <> LT.link_id. У вас есть таблица записей за 70 миллионов, вам не нужно обновлять записи, которые не нуждаются в изменении. То же самое, конечно, относится и к вашим другим обновлениям.

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

+1

Я согласен с концепцией пакета, поскольку он также освобождает журналы транзакций, которые будут повторно использоваться для этой транзакции, а также любые другие транзакции, происходящие на сервере. Обычно я рекомендую размер партии менее 5000, но из-за эскалации блокировки, которая в противном случае перерастает в эксклюзивную блокировку таблицы и, возможно, придется ждать, пока какие-либо текущие блокировки других транзакций будут выпущены. «update top (n)» также делает процесс дозирования довольно простым. –

0

В дополнение ко всем ответам выше.

i) Даже 3 часа много. Я имею в виду, даже если какой-либо запрос занимает 3 часа, я сначала проверяю свое требование и пересматриваю его. Исследуйте проблему. Конечно, я буду оптимизировать свой запрос. Как и в вашем запросе, ни одно обновление не является серьезным вопросом.

Как @Devart указал, один из столбцов можно рассчитать столбцы.

ii) Попробуйте запустить другой запрос на новом сервере и сравнить.?

iii) Восстановить индекс.

iv) Используйте «с (nolock)» в вашем соединении.

v) Создать индекс в таблице LookUp_ABC_20142015 column Link_ref.

vi) сгруппированный индекс на nvarchar (17) или datetime всегда плохая идея. присоединиться к столбцу datetime или столбцу varchar всегда требуется время.

0

Попробуйте с псевдонимом вместо улавливания имени таблицы в запросе UPDATE

USE [ABCJan] 
CREATE INDEX Link_Oct ON ABCJan2014 (Link_ref) 
GO 
CREATE INDEX Day_Oct ON ABCJan2014 (date_1) 
GO 

UPDATE MT 
SET  MT.link_id = LT.link_id 
FROM  ABCJan2014 MT 
INNER JOIN [Central].[dbo].[LookUp_ABC_20142015] LT 
ON MT.Link_ref = LT.Link_ref 

UPDATE ABCJan2014 
SET  SumAvJT = av_jt * n 

UPDATE MT 
SET  MT.DayType = LT2.DayType 
FROM  ABCJan2014 MT 
INNER JOIN [Central].[dbo].[ABC_20142015_days] LT2 
ON MT.date_1 = LT2.date1 
1

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

Сказали, на каком объеме данных вы смотрите? Если вы посмотрите на значительно разные объемы данных, может быть, что планы выполнения, сгенерированные серверами, значительно различаются. SQL Server не всегда правильно угадывает, когда он строит планы.

Также вы используете подготовленные операторы (т. Е. Хранимые процедуры)? Если да, то возможно, что план доступа к кэшированным данным просто устарел. & необходимо обновить или вам необходимо обновить статистику по таблицам, а затем запустите процедуру with recompile, чтобы создать новый план доступа к данным.

2

Почему есть 3 оператора обновлений, когда вы можете сделать это в одном?

UPDATE MT 
SET  MT.link_id = CASE WHEN LT.link_id IS NULL THEN MT.link_id ELSE LT.link_id END, 
     MT.SumAvJT = MT.av_jt * MT.n, 
     MT.DayType = CASE WHEN LT2.DayType IS NULL THEN MT.DayType ELSE LT2.DayType END 
FROM  ABCJan2014 MT 
LEFT OUTER JOIN [Central].[dbo].[LookUp_ABC_20142015] LT 
    ON MT.Link_ref = LT.Link_ref 
LEFT OUTER JOIN [Central].[dbo].[ABC_20142015_days] LT2 
    ON MT.date_1 = LT2.date1 

Кроме того, я бы создал только один индекс для соединения. Создайте следующий индекс после обновлений.

CREATE INDEX Day_Oct ON ABCJan2014 (date_1) 
GO 

Перед тем, как запустить, сравнить план выполнения, поставив запрос обновления выше, и ваше 3 заявления обновления вообще в одном окне запроса, и сделать дисплей Предполагаемого плана выполнения. Он покажет оценочные проценты, и вы сможете узнать, лучше ли это (если новый - < 50%).

Кроме того, похоже, что запрос выполняется медленно, потому что он выполняет Hash Match. Добавьте индекс PK в [LookUp_ABC_20142015] .Link_ref.

[LookUp_ABC_20142015] .Link_ID - плохой выбор для ПК, поэтому оставьте PK в этой колонке.

Затем добавьте указатель на [ABCJan2014] .Link_ref.

Проверьте, не улучшилось ли это.

+0

В зависимости от данных это MIGHT не делает то же самое, что делает это в трех отдельных обновлениях. Довольно упрощенный (и преувеличенный) пример: представьте, что LT только «совпадает» с первой половиной таблицы, а LT2 «соответствует» со второй половиной, объединенное обновление будет иметь 0 записей! – deroby

+0

Я вижу вашу точку зрения. Обновлен мой ответ. – joordan831

1

где находится [Центральный] сервер? Можно дублировать таблицу [Central]. [Dbo]. [LookUp_ABC_20142015] и [Central]. [Dbo]. [ABC_20142015_days] локально?

1) У:

select * into [ABC_20142015_days] from [Central].[dbo].[ABC_20142015_days] 
    select * into [LookUp_ABC_20142015] from [Central].[dbo].[LookUp_ABC_20142015] 

2) Воссоздать индекс [ABC_20142015_days] и [LookUp_ABC_20142015] ...

3) Перепишите ваши обновления путем удаления «[Центральный] [DBO. ] «. префикс !

Сразу после написания этого решения я нашел другое решение, но не уверен, что оно применимо к вашему серверу: добавьте подсказки для соединения «REMOTE» ... Я никогда не использую его, но вы можете найти документацию в https://msdn.microsoft.com/en-us/library/ms173815.aspx

Hopping это может помочь ...

0

Честно говоря, я думаю, вы уже ответили на свой вопрос.

ABCJan2014 (70 million rows - NO UNIQUE IDENTIFIER - Link_ref & date_1 together are unique)

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

Query Plan showing the need for an index on [ABCJAN2014].[date_1] 3 times in a row!

Вы не должны верить всему, что MSSQL говорит вам, но вы должны по крайней мере дать ему попробовать =)

Комбинирование как я хотел бы предложить вам добавить PK к столу на поля [date_1] и [Link_ref] (в этом порядке!). Разум: добавление Первичного ключа, который по существу является кластеризованным уникальным индексом, займет некоторое время и потребует много места, так как таблица в значительной степени дублируется на этом пути.

Что касается вашего запроса, вы можете поместить все 3 обновления в 1 оператор (похоже на то, что предлагает joordan831), но вы должны позаботиться о том, что JOIN может ограничить количество затронутых строк. Таким образом, я бы переписать так:

UPDATE ABCJan2014 
SET ABCJan2014.link_id = (CASE WHEN LT.Link_ref IS NULL THEN ABCJan2014.link_id ELSE LT.link_id END), -- update when there is a match, otherwise re-use existig value 
     ABCJan2014.DayType = (CASE WHEN LT2.date1 IS NULL THEN ABCJan2014.DayType ELSE LT2.DayType END), -- update when there is a match, otherwise re-use existig value 
     SumAvJT   = ABCJan2014.av_jt * ABCJan2014.n 

FROM  ABCJan2014 MT 
LEFT OUTER JOIN [Central].[dbo].[LookUp_ABC_20142015] LT 
      ON MT.Link_ref = LT.Link_ref 

LEFT OUTER JOIN [Central].[dbo].[ABC_20142015_days] LT2 
      ON MT.date_1 = LT2.date1 

, который должен иметь тот же эффект, как работает ваш первоначальный 3 обновления последовательно; но, надеюсь, потребуется намного меньше времени.

PS: Судя по планам запросов, у вас уже есть индексы на таблицах вы присоединитесь к ([LookUp_ABC_20142015] & [LookUp_ABC_20142015]), но они, кажется, не единственный (и не всегда кластерный). Предполагая, что они страдают от «мы знаем, что это уникально, но сервер не делает» - болезнь: было бы целесообразно также добавить первичный ключ к этим таблицам в областях, к которым вы присоединяетесь, как для целостности данных, так и для производительности !

Удачи.

+0

Согласен. Первое, что вам нужно сделать, это добавить правильные ключи. Однако, даже если он забыл добавить ПК, все еще странно, что разница во времени такова, что большой. Таким образом, есть и другая причина разницы. – HappyMe

+0

Я видел эти виды замедлений раньше. Существуют две «нормальные» причины этого: (1) отвратительная фрагментация таблицы/индекса и (2) порог неожиданного ресурса. # 1 можно значительно улучшить, добавив кластерный индекс в любую из трех таблиц, которые его не имеют, и перестроить кластеризованный индекс в любой таблице, которая его имеет. (продолжение ниже) –

1

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

Однако ваш вопрос в том, почему ТОЧНЫЕ данные/структура и ТОЛЬКО ПРОБЛЕМЫ дают эту огромную разницу.

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

Только тогда, когда вы решили реальную проблему, вы можете начать улучшение самого SQL

+0

Как уже упоминал Laughin Vergil; вероятная причина заключается в том, что некоторый порог пропускается, и сервер работает сам по себе. Я видел это раньше, запрос выполняется на одном компьютере и совершенно по-другому на другой машине для одних и тех же данных. Вдаваясь в это часто (но не всегда!) Показывает, что есть что-то очень неоптимальное в отношении того, как все настроено и что, похоже, путает оптимизатор; выбирая рабочий путь на одной машине и совершенно неправильный с другой. Фиксация «макета вещей» часто приводит к QO, чтобы выбрать план, который работает в обоих случаях. – deroby

+0

Длинный ответ короткий: Оптимизатор запросов иногда может быть черным ящиком и вместо того, чтобы тратить время на то, почему все работает не так, как вы ожидали бы их (здесь есть множество переменных!); вероятно, легче научиться «играть хорошо» в сторону QO и выяснить, как заставить его работать на вас. – deroby

Смежные вопросы