Мы уже давно используем наши базы данных в COMPATIBILITY_LEVEL <120, поскольку не можем точно определить, почему некоторые запросы выполняются очень медленно. Не думаю, что это идеальное лекарство от скуки covid-19, чтобы исправить это, и, следовательно, я пытаюсь решить проблемы нового (эхх ... уже не такого нового) CE. </p>
Итак, я получил довольно простой запрос, включающий 3 таблицы и 2 параметра значения таблицы
declare @spIDs as table (id int not null primary key);
INSERT INTO @spIDs values(1),(2); -- 169 in my sql script
DECLARE @subscriptionProductGroupMapping AS table
(
subscriptionProductID int not null,
groupID int not null,
primary key(subscriptionProductID, groupID)
);
INSERT INTO @subscriptionProductGroupMapping (subscriptionProductID, groupID)
VALUES(101,101); -- 168 in my script
SELECT
[dbo].[User].[userID]
FROM
[dbo].[User]
LEFT JOIN
(
SELECT DISTINCT [UserValidThrough].userID
FROM
[dbo].[UserValidThrough]
INNER JOIN @spIDs spIDs on(spIDs.id = [dbo].[UserValidThrough].subscriptionProductID)
) AS [uvt] ON [uvt].[userID] = [User].userID
INNER JOIN
(
SELECT DISTINCT userID
FROM
GroupMembership
INNER JOIN
@subscriptionProductGroupMapping as SPIAndGroup ON(GroupMembership.groupID = SPIAndGroup.groupID)
) gms ON(gms.userID = [User].userID)
WHERE
[User].permissionType NOT IN(8, 16, 32, 64, 128) AND
[User].deleteDate IS NULL AND
[User].userTypeID IN(@userTypeID_0) AND
[uvt].[userID] IS NULL
При выполнении этого запроса с уровнем совместимости 110 запрос выполняется молниеносно, менее 0,3 секунды. При переходе на уровень совместимости 120 выполнение запроса занимает около 8-9 секунд! : (
При исследовании фактического плана выполнения я вижу, что есть один кластеризованный поиск индекса, который выделяет около 7 секунд, поэтому я сосредотачиваюсь на этой части
Я пытался разбить этот запрос на другой параметр значения таблицы, а затем запрос снова выполняется быстро, однако это будет означать, что мне придется переписать мое приложение на многих мест, и мне бы очень хотелось узнать, почему это происходит медленно и что с этим делать.
Может кто-нибудь пролить свет на эту проблему?
Редактировать 2020-04-15 12:57 CET Это сценарий отладки, который воспроизводит упрощенный сценарий: 1. Создание таблиц отладки и данных отладки
create table temp_User
(
userID int not null,
-- Additional columns ommited for readability
primary key (useriD)
);
create table temp_UserValidThrough
(
userID int not null,
subscriptionProductID int not null,
-- Additional columns ommited for readability
primary key(userID, subscriptionProductID)
);
create table temp_GroupMembership
(
userID int not null,
groupID int not null,
primary key(userID, groupID)
);
CREATE NONCLUSTERED INDEX temp_GroupMembership_GroupIDWithUserID
ON [dbo].[temp_GroupMembership] ([groupID])
INCLUDE ([userID])
-- populate User
-- populate UserValidThrough
-- populate GroupMembership
declare @noUsers as int = 120000;
declare @noGroups as int = 400;
SET NOCOUNT ON;
declare @n as int = 0;
while @n < @noUsers
begin
insert into temp_User values(@n);
declare @rand as int = rand() * @noGroups;
insert into temp_UserValidThrough VALUES(@n, @rand);
insert into temp_GroupMembership VALUES(@n, @rand);
SET @n = @n + 1;
end;
Этот запрос может быть выполнен с различными настройками COMPATIBILITY_LEVEL, и производительность будет сильно отличаться.
DECLARE @userTypeID_0 AS Int;
SET @userTypeID_0 = '1';
declare @spIDs as table (id int not null primary key);
insert into @spIDs
select distinct groupID from temp_GroupMembership;
DECLARE @subscriptionProductGroupMapping AS table
(
subscriptionProductID int not null,
groupID int not null,
primary key(subscriptionProductID, groupID)
);
insert into @subscriptionProductGroupMapping
SELECT
T.groupID as subscriptionProductID,
T.groupID
FROM
(select distinct groupID from temp_GroupMembership) AS T;
SELECT
[User].[userID]
FROM
[dbo].[temp_User] AS [User]
--LEFT JOIN
--(
-- SELECT DISTINCT [UserValidThrough].userID
-- FROM
-- [dbo].[UserValidThrough]
-- INNER JOIN @spIDs spIDs on(spIDs.id = [dbo].[UserValidThrough].subscriptionProductID)
--) AS [uvt] ON [uvt].[userID] = [User].userID
INNER JOIN
(
SELECT DISTINCT userID
FROM
temp_GroupMembership as GroupMembership
INNER JOIN
@subscriptionProductGroupMapping as SPIAndGroup ON(GroupMembership.groupID = SPIAndGroup.groupID)
) gms ON(gms.userID = [User].userID)
WHERE
--[User].permissionType NOT IN(8, 16, 32, 64, 128) AND
--[User].deleteDate IS NULL AND
--[User].userTypeID IN(@userTypeID_0) AND
NOT EXISTS
(
SELECT DISTINCT [UserValidThrough].userID
FROM
[dbo].[UserValidThrough]
--INNER JOIN @spIDs spIDs on(spIDs.id = [dbo].[UserValidThrough].subscriptionProductID)
WHERE
[UserValidThrough].userID = [User].userID
AND
[UserValidThrough].subscriptionProductID IN(SELECT id from @spIDs)
)
--[uvt].[userID] IS NULL
declare @lvl as varchar (10) = (SELECT compatibility_level FROM sys.databases WHERE name = DB_NAME());
print 'COMPATIBILITY_LEVEL: ' + @lvl
-- COMPATIBILITY_LEVEL 110 time: average 242 ms
-- COMPATIBILITY_LEVEL 120 time: average 40 s! (200 times slower!)