#sql #sql-server #tsql #query-performance
#sql #sql-сервер #tsql #запрос-производительность
Вопрос:
У меня ситуация, когда предполагаемое количество строк в плане выполнения отклонено
Мои столбцы в соединении varchar(50)
. Я пробовал разные индексы, но это не уменьшает эту проблему. Я даже пробовал использовать индекс в таблице temp. Что еще я могу сделать?
PS это первое место, где расчетное число начинает смещаться… Также таблицы невелики (48000 строк).
Код:
SELECT DISTINCT householdnumber, householdid, primaryCustomerID
INTO #Households
FROM TableA
SELECT
A.*,
MIN(B.[ProfileCreatedDate]) PROFILECREATEDDATE
INTO #Profile
from #Households AS a
LEFT JOIN TableA AS B
ON A.[HouseholdNumber]= B.[HouseholdNumber] and A.[HouseholdId]=B.[HouseholdId]
GROUP BY a.householdnumber, a.householdid, a.primaryCustomerID;
Я знаю, кажется, что это можно переписать как:
SELECT householdnumber, householdid, primaryCustomerID, MIN([ProfileCreatedDate]) AS PROFILECREATEDDATE
INTO #Profile2
from TableA
GROUP BY householdnumber, householdid, primaryCustomerID;
Но результаты не идентичны, и я не хочу изменять результаты, поскольку я не уверен, знал ли создатель этого кода, что они делали.
Некоторая статистика по столбцам: householdnumber
всегда равно householdid
. householdid
есть nvarchar(50)
, но householdnumber
есть varchar(40)
. В таблице 48877 строк. Отдельная комбинация householdnumber, householdid, primaryCustomerID
имеет 48029 строк. И отдельное число primaryCustomerID
равно 47152.
Комментарии:
1. Не понимая всего, что происходит, я не уверен, что это полезно, но: знаете ли вы, что можете запускаться
UPDATE STATISTICS #Households
как часть вашего скрипта? Или, возможно, лучше, добавитьOPTION (RECOMPILE)
в свой запрос (послеGROUP BY
строки).2. @EdmCoff: Спасибо, я постараюсь
UPDATE STATISTICS
. На самом деле я пытался перекомпилировать, но это тоже не помогло. Я дам вам знать, если это сработает. Но таблица только что создана? Мне нужно больше узнать о статистике…3. @EdmCoff: обновление статистики не помогло…
4. Пожалуйста, смотрите раздел Вставка плана , чтобы узнать, как включить план выполнения в ваш вопрос.
5. @HABO: Я бы с удовольствием, но я немного параноик, чтобы публиковать что-то конфиденциальное…
Ответ №1:
Что касается кода — похоже, что разница между более крупной (исходной) версией и вашей более простой группой ПО версии заключается в том, что оригинал находит минимум profilecreateddate
для любого в этом домохозяйстве, тогда как ваша более простая версия находит profilecreateddate
для конкретного primarycustomerid
.
Например (с использованием более простых данных)
CREATE TABLE #TableA (householdnumber int, householdid int, primaryCustomerID int, ProfileCreatedDate datetime);
INSERT INTO #TableA (householdnumber, householdid, primaryCustomerID, ProfileCreatedDate) VALUES
(1, 1, 1, '20201001'),
(1, 1, 1, '20201002'),
(1, 1, 2, '20201003');
SELECT DISTINCT householdnumber, householdid, primaryCustomerID
INTO #Households
FROM #TableA;
SELECT
A.*,
MIN(B.[ProfileCreatedDate]) PROFILECREATEDDATE
INTO #Profile
from #Households AS a
LEFT JOIN #TableA AS B
ON A.[HouseholdNumber]= B.[HouseholdNumber] and A.[HouseholdId]=B.[HouseholdId]
GROUP BY a.householdnumber, a.householdid, a.primaryCustomerID;
SELECT * FROM #Profile;
/* -- Results
householdnumber householdid primaryCustomerID PROFILECREATEDDATE
1 1 1 2020-10-01 00:00:00.000
1 1 2 2020-10-01 00:00:00.000
*/
SELECT householdnumber, householdid, primaryCustomerID, MIN([ProfileCreatedDate]) AS PROFILECREATEDDATE
INTO #Profile2
from #TableA
GROUP BY householdnumber, householdid, primaryCustomerID;
SELECT * FROM #Profile2;
/* -- Results
householdnumber householdid primaryCustomerID PROFILECREATEDDATE
1 1 1 2020-10-01 00:00:00.000
1 1 2 2020-10-03 00:00:00.000
*/
Если вы заметили выше, PROFILECREATEDATE для строки 2 отличается.
Поэтому вы можете попробовать следующий код, который должен давать те же результаты, что и исходный набор — посмотрите, как это работает со временем (и подтвердите, что оно соответствует исходным результатам).
SELECT DISTINCT t1.householdnumber, t1.householdid, primaryCustomerID,
MIN([ProfileCreatedDate]) OVER (PARTITION BY t1.householdnumber, t1.householdid) AS PROFILECREATEDDATE
INTO #Profile3
FROM #TableA t1;
SELECT * FROM #Profile3;
/* -- Results
householdnumber householdid primaryCustomerID PROFILECREATEDDATE
1 1 1 2020-10-01 00:00:00.000
1 1 2 2020-10-01 00:00:00.000
*/
Комментарии:
1. Вы правы. Спасибо! результаты идентичны с использованием
(SELECT * FROM #Profile EXCEPT SELECT * FROM #Profile2) UNION all (SELECT * FROM #Profile2 EXCEPT SELECT * FROM #Profile)
2. Теперь это очень быстро. Иногда это занимало около 10 минут, прежде чем оно заканчивалось за 2 секунды. Но почему оценки до сих пор в исходном коде?
3. 1 за добавление опции функции window, которая должна решить исходную проблему OPs, а также помогает обучить тому, как использовать SQL Server в соответствии с его сильными сторонами
4. Что касается оценок — часто бывает нелегко понять, что происходит, но помните, что оценки основаны только на небольшом количестве метаданных , без фактического просмотра данных. Часто группировка сильно искажает оценки, потому что нужно просто угадать, сколько получится (например, если группировка, давайте предположим, что результат будет составлять 20% от размера). Я не знаю на 100% (не глядя на план выполнения в целом), почему это так далеко. Решение, которое у меня было там: а) минимизирует чтение (только один раз считывает таблицу), а затем просто группирует в конце, чтобы минимизировать группировку, вызывающую многократные ошибки.
5. Хорошо, спасибо. На самом деле метод оконной функции также делает более понятным содержание кода, в то время как исходный код трудно понять. В любом случае спасибо.