【发布时间】:2016-04-21 00:56:52
【问题描述】:
我已经为我的 Facebook 应用程序创建了 MS SQL 2014 数据库,可以与朋友一起使用。我在数据库中为我的所有用户保留朋友,并在应用程序启动时从 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;
问题是查询优化器无法识别它可以在 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);
【问题讨论】:
-
您是否尝试过指定 MERGE 提示(
MERGE UserFriends WITH(INDEX(<index to seek>)) ...或MERGE UserFriends WITH(FORCESEEK(<index to seek>)) ...? -
我试过 FORCESEEK。查询将失败 - 优化器无法找到可用的索引。
-
尝试将 OPTION (RECOMPILE) 添加到合并语句的末尾。 SQL 优化器将使用您第一次运行它时指定的第一个 @UserId 来生成缓存计划。选项(重新编译)将使用实际值来生成计划,请注意,每次运行语句时重新编译都会产生少量开销。
-
我尝试了多种变体,包括强制索引、强制搜索和 OPTION(RECOMPILE)。由于优化器已经使用索引,我希望它使用(聚集主键)强制它什么都不改变。强迫它去 SEEK 使它失败。 RECOMPILE 没有任何改变。在我看来,要么需要更改语法以让 oprimizer 知道它是那种查询,要么 SQL 服务器没有实现该选项。
-
还有其他建议吗? :)
标签: sql sql-server indexing merge seek