Joz*_*sky 2 sql sql-server indexing merge seek
我为我的 Facebook 应用程序制作了 MS SQL 2014 数据库,该应用程序与朋友一起使用。我在 DB 中为所有用户保留好友,并在应用程序启动时从 Facebook 更新他们。为此,我使用了 MERGE 语句(表变量 @FriendUserIds 包含好友 ID 列表;表 UserFriends 具有聚集主键(UserId、FriendUserId)):
MERGE UserFriends
USING (
SELECT
UserId
FROM @FriendUserIds
) AS source (FriendUserId)
ON UserFriends.UserId = @UserId
AND UserFriends.FriendUserId = source.FriendUserId
WHEN NOT MATCHED BY TARGET
THEN INSERT (UserId, FriendUserId)
VALUES (@UserId, source.FriendUserId)
WHEN NOT MATCHED BY SOURCE
AND UserFriends.UserId = @UserId
THEN DELETE;
Run Code Online (Sandbox Code Playgroud)
问题是查询优化器无法识别它可以在 UserFriends 上使用 INDEX SEEK。它使用 SCAN 代替,我不知道强制 SEEK 的方法。现在,我通过将操作拆分为两个查询(用于添加新朋友的 MERGE 和用于删除不再朋友的 DELETE)来规避该问题,这仍然比单个 MERGE 语句快得多(没有 DELETE 语句的 MERGE 使用 SEEK):
DELETE
FROM UserFriends
WHERE UserFriends.UserId = @UserId
AND UserFriends.FriendUserId NOT IN (
SELECT
UF.UserId
FROM @FriendUserIds UF
)
MERGE UserFriends
USING (
SELECT
UserId
FROM @FriendUserIds
) AS source (FriendUserId)
ON UserFriends.UserId = @UserId
AND UserFriends.FriendUserId = source.FriendUserId
WHEN NOT MATCHED BY TARGET
THEN INSERT (UserId, FriendUserId)
VALUES (@UserId, source.FriendUserId);
Run Code Online (Sandbox Code Playgroud)
尝试使用通用表表达式(CTE)作为您的“目标”:
;WITH UserFriends_CTE
AS (SELECT [UserID],
[FriendUserID]
FROM [UserFriends]
WHERE [UserID] = @UserId)
MERGE UserFriends_CTE
USING (SELECT [UserId]
FROM @FriendUserIds) AS source ([FriendUserId])
ON UserFriends_CTE.[UserId] = @UserId
AND UserFriends_CTE.[FriendUserId] = source.[FriendUserId]
WHEN NOT MATCHED BY TARGET THEN
INSERT ([UserId],
[FriendUserId])
VALUES (@UserId,
source.[FriendUserId])
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
Run Code Online (Sandbox Code Playgroud)
MERGE
语句的性能通常比拆分为多个语句更差,并且MERGE 存在一些已知问题。根据 Paul White 在此答案中的说法,使用 CTE可能会导致问题,因此请对其进行测试。
如果您确实使用拆分版本,我将如何实现它:
DELETE uf
FROM [UserFriends] uf
WHERE uf.[UserId] = @UserId
AND NOT EXISTS
(SELECT 1
FROM @FriendUserIds fu
WHERE uf.[FriendUserId] = fu.[FriendUserId]);
INSERT INTO [UserFriends]
([UserId],
[FriendUserId])
SELECT @UserId,
fu.[FriendUserId]
FROM @FriendUserIds fu
WHERE NOT EXISTS
(SELECT 1
FROM [UserFriends] uf
WHERE fu.[FriendUserId] = uf.[FriendUserId]
AND uf.[UserId] = @UserId);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2299 次 |
最近记录: |