我正在SQL / C#中实现用户搜索功能。我在功能本身的后勤方面遇到了一些麻烦,我正在寻找一些指导。
我一直在使用三嵌套子查询,我觉得它运行有点慢(平均280-300ms从Users表中的1000条记录中提取结果)。
我首先要按用户的属性(单独的表)进行搜索,然后根据位置进行搜索。然后,我想要计算和排序距离,然后一次只返回最接近的10条记录(分页结果)。
我是否使用三重嵌套子查询使用正确的方法?或者是否有一个标准或指导原则来做这种事情(双关语不是那种)?
代码示例:
SELECT * FROM
(SELECT *, ROW_NUMBER()
OVER (ORDER BY UsersSubquery.Distance ASC) as RowNumber
FROM
(
SELECT TOP 100 PERCENT UsersSubquery.*,
(((geography::Point(UsersLocationsSubquery.Latitude, UsersLocationsSubquery.Longitude, 4326)).STDistance(@a)) / 1000) AS Distance
FROM Users UsersSubquery
INNER JOIN UsersAndAttributes ON UsersSubquery.UserId = UserAndAttributes.UserId
INNER JOIN UsersAndLocations AS UsersWithLocationsSubquery ON UsersSubquery.UserId = UsersWithLocationsSubquery.UserId
WHERE
(
((@Attribute1 = NULL) OR (UsersSubquery.Attribute1Id = @Attribute1))
AND
((@Attribute2 = NULL) OR (UsersSubquery.Attribute2Id = @Attribute2))
AND
((@Attribute3 = NULL) OR (UsersSubquery.Attribute3Id = @Attribute3))
AND
...etc
)
)
AS UsersSubquery )
Users
INNER JOIN Pictures ON Users.UserId = Pictures.UserId
WHERE RowNumber >= @StartRow and RowNumber <= @EndRow
Order by RowNumber
答案 0 :(得分:2)
因为您想要分页数据,这意味着您需要过滤需要通过Distance
计算排序的ROW_NUMBER()值,您将需要3个嵌套的子查询。但是,我确实认为它可以做得更干净。
我不确定为什么你从你们其中一个人那里获取TOP 100 PERCENT
你的内心疑问,因为那实际上并没有做任何事情。我只能假设您在某个时刻订购了内部查询并使用TOP子句使其不会出错。 这不起作用,并且结果的顺序无法保证。因此,从那时起你看起来已经采用了不同的方式,这很好。
您在查询中使用别名的方式也有点令人困惑。在最里面的查询中,您将实际的User
表别名为UsersSubquery
,然后您为具有相同名称的后续子查询别名。最后,您将最外面的子查询别名为Users
,其名称与基表相同。
在您进行其他加入的同时,没有理由不加入Pictures
表。在该连接和最里面的查询(具有过滤用户属性的WHERE
子句)之间没有任何额外的过滤,所以进行连接也是一样的。
这就是我编写查询的方式:
DECLARE @RowsToFetch INT = 10;
SELECT TOP (@RowsToFetch) *
FROM (
SELECT *,
ROW_NUMBER() OVER (ORDER BY Distance ASC) AS RowNumber
FROM (
SELECT Users.*,
(((geography::Point(UsersAndLocations.Latitude, UsersAndLocations.Longitude, 4326)).STDistance(@a)) / 1000) AS Distance
FROM Users
INNER JOIN UsersAndAttributes ON Users.UserId=UsersAndAttributes.UserId
INNER JOIN UsersAndLocations ON Users.UserId=UsersAndLocations.UserId
INNER JOIN Pictures ON Users.UserId=Pictures.UserId
WHERE (@Attribute1 = NULL OR UsersAndAttributes.Attribute1Id = @Attribute1)
AND (@Attribute2 = NULL OR UsersAndAttributes.Attribute1Id = @Attribute2)
..etc
) AS UsersData
) AS UsersNumbered
WHERE RowNumber >= @StartRow
ORDER BY RowNumber
最后,我实际上在查询中的任何地方使用SELECT *
(尤其是最里面的子查询)。而是仅选择所需的特定列。这将有助于减少SQL Server需要执行的工作量,并减少返回结果时需要通过网络移动的数据量。我不知道你需要哪些列,所以我重新使用了你的SELECT *
。