我们已经在 COMPATIBILITY_LEVEL < 120 中运行数据库有一段时间了,因为我们无法确定某些查询运行速度非常慢的原因。不是我觉得这是解决 covid-19 无聊问题的完美方法,因此我尝试解决新的(嗯……不再那么新)CE 的问题。
所以,我有一个相当简单的查询,涉及 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!)