我有以下存储过程,它在没有返回总记录的情况下工作得很好,但是当我在单独查询中返回总记录时,它变得很慢。
还有另一种以更快的方式返回总记录的方法
视图
SELECT dbo.tbl_ImagesMaster.ImgTitle, dbo.tbl_ImagesMaster.ImgID, dbo.tbl_ImagesMaster.ImgDate, dbo.tbl_ImagesMaster.ImgActive,
dbo.tbl_ImagesMaster.ImgHits, dbo.aspnet_Users.UserName, dbo.tbl_ImagesMaster.ImgUserID, dbo.tbl_ImagesDetails.ImgDPath,
dbo.tblUsersExtended.UAvatar, dbo.tbl_ImagesMaster.ImgRemarks
FROM dbo.tbl_ImagesMaster INNER JOIN
dbo.aspnet_Users ON dbo.tbl_ImagesMaster.ImgUserID = dbo.aspnet_Users.UserId INNER JOIN
dbo.tbl_ImagesDetails ON dbo.tbl_ImagesMaster.ImgID = dbo.tbl_ImagesDetails.ImgDParentID INNER JOIN
dbo.tblUsersExtended ON dbo.aspnet_Users.UserId = dbo.tblUsersExtended.UUserID
WHERE (dbo.tbl_ImagesDetails.ImgDDefault = 1)
THE SP
ALTER Procedure [dbo].[usp_searchImagestest]
(
@CurrentPage As int=1,
@PageSize As int=10,
@searchType As int=1,
@Activeflag As bit=null,
@searchTerm as nvarchar(200),
@TotalRecords As int OUTPUT
)
As
begin
-- Turn off count return.
Set NoCount On
--Full text search
if @searchType=1
begin
select
ImgID,ImgTitle,ImgDate,imgHits,UserName,uavatar,RowRank
from
(Select
ImgID,imgtitle,ImgDate,imghits,UserName,UAvatar
, ROW_NUMBER() OVER (ORDER BY ImgID desc) AS RowRank
From
vw_MasterImagesSearch
WHERE FREETEXT(*, @searchTerm) and (@Activeflag IS NULL or ImgActive=@Activeflag)
) as MyImages
where
RowRank > (@PageSize * (@CurrentPage-1)) AND RowRank <= (@CurrentPage * @PageSize)
-- TOTAL RECORD, WHICH SLOW DOWN THE WHOLE SP EXECUTION
select @TotalRecords = (select COUNT(*) From vw_MasterImagesSearch
WHERE FREETEXT(*, @searchTerm) and (@Activeflag IS NULL or ImgActive=@Activeflag))
end
我试过这个
查询1
select
ImgID,ImgTitle,ImgDate,imgHits,RowRank,TOTALCOUNT,
(select username from aspnet_Users where UserId=MyImages.imguserid) as username,
(select UAvatar from tblUsersExtended where UUserID=MyImages.imguserid) as Uavatar
from
(Select
ImgID,imgtitle,ImgDate,imghits,ImgUserID
, ROW_NUMBER() OVER (ORDER BY ImgID desc) AS RowRank
,COUNT(*) OVER() AS TOTALCOUNT
From
tbl_ImagesMaster
WHERE FREETEXT(ImgTitle, @searchTerm) and (@Activeflag IS NULL or ImgActive=@Activeflag)
) as MyImages
where
RowRank > (@PageSize * (@CurrentPage-1)) AND RowRank <= (@CurrentPage * @PageSize)
查询2
select
ImgID,ImgTitle,ImgDate,imgHits,RowRank,
(select username from aspnet_Users where UserId=MyImages.imguserid) as username,
(select UAvatar from tblUsersExtended where UUserID=MyImages.imguserid) as Uavatar
from
(Select
ImgID,imgtitle,ImgDate,imghits,ImgUserID
, ROW_NUMBER() OVER (ORDER BY ImgID desc) AS RowRank
From
tbl_ImagesMaster
WHERE FREETEXT(ImgTitle, @searchTerm) and (@Activeflag IS NULL or ImgActive=@Activeflag)
) as MyImages
where
RowRank > (@PageSize * (@CurrentPage-1)) AND RowRank <= (@CurrentPage * @PageSize)
select @TotalRecords = (select COUNT(*) From tbl_ImagesMaster
WHERE FREETEXT(ImgTitle, @searchTerm) and (@Activeflag IS NULL or ImgActive=@Activeflag))
查询1耗时3秒 查询2花了不到1秒
表中的总行数为100,000行
答案 0 :(得分:3)
第二个选择再次运行搜索。只需更改您的分页搜索:
SELECT
ImgID,ImgTitle,ImgDate,imgHits,UserName,uavatar,RowRank, TOTALCOUNT
FROM
(SELECT
ImgID,
imgtitle,
ImgDate,
imghits,
UserName,
UAvatar,
ROW_NUMBER() OVER (ORDER BY ImgID desc) AS RowRank
COUNT(*) OVER() AS TOTALCOUNT
FROM
vw_MasterImagesSearch
WHERE FREETEXT(*, @searchTerm) and (@Activeflag IS NULL or ImgActive=@Activeflag)
) as MyImages
WHERE
RowRank > (@PageSize * (@CurrentPage-1)) AND RowRank
The line
COUNT(*) OVER() AS TOTALCOUNT
在分页发生之前给出内部SELECT的计数。
答案 1 :(得分:0)
我认为应该在iKnowKungFoo解决方案中改变部分
ROW_NUMBER() OVER (ORDER BY ImgID desc) AS RowRank
COUNT(*) OVER() AS TOTALCOUNT
到
ROW_NUMBER() OVER (ORDER BY ImgID desc) AS RowRank
ROW_NUMBER() OVER (ORDER BY ImgID) AS TotalRows
所以第二部分应该包含 reverse 顺序。您可以阅读this answer和the article(需要注册到最后一个)。
无论如何,我个人会使用CTE而不是子查询。在这种方式中,您可以将所有行定义为主要集合作为CTE。您需要的只是执行一次获取COUNT(*)
并另外获取所需的数据页面WHERE seq BETWEEN (@PageSize * (@CurrentPage-1)) AND (@CurrentPage * @PageSize)
另一种替代方案可以采用CROSS JOIN
方式代替ROW_NUMBER() OVER (<reverse sort order>)
(请参阅here)或UNION
方法(请参阅here)。我建议你测试所有的方法,找到最合适的方法。
更新:我对CTE的意思是,可以更好地优化视图。首先,您可以尝试以下查询
WITH MasterImagesSearch AS (
SELECT im.ImgTitle, im.ImgID, im.ImgDate, im.ImgActive,
im.ImgHits, asp.UserName, im.ImgUserID, id.ImgDPath,
e.UAvatar, im.ImgRemarks
FROM dbo.tbl_ImagesMaster AS im INNER JOIN
dbo.aspnet_Users AS asp ON im.ImgUserID = asp.UserId INNER JOIN
dbo.tbl_ImagesDetails AS id ON im.ImgID = id.ImgDParentID INNER JOIN
dbo.tblUsersExtended AS e ON asp.UserId = e.UUserID
WHERE id .ImgDDefault = 1
), GetAll AS (
SELECT ImgID,ImgTitle,ImgDate,imgHits,UserName,uavatar
FROM MasterImagesSearch
WHERE FREETEXT(ImgTitle, @searchTerm)
AND (@Activeflag IS NULL or ImgActive=@Activeflag)
), MyImages AS (
SELECT ImgID,imgtitle,ImgDate,imghitsUserName,uavatar
,ROW_NUMBER() OVER (ORDER BY ImgID desc) AS RowRank
,ROW_NUMBER() OVER (ORDER BY ImgID) AS TotalRows
FROM GetAll
)
SELECT ImgID,imgtitle,ImgDate,imghitsUserName,uavatar
,RowRank + TotalRows -1 AS Total
FROM MyImages
WHERE RowRank BETWEEN (@PageSize * (@CurrentPage-1)) AND (@CurrentPage * @PageSize)
我没有进行任何额外的优化,但我使用CTE和INNER JOIN
而不是子查询。
答案 2 :(得分:0)
我认为当您使用存储过程通过参数获取查询结果时,SQL Server会为此SP生成执行计划,并为之后调用SP的每个SP使用此生成的执行计划。生成执行计划的有趣之处是表统计,SQL Server使用它们来生成最佳执行计划。如果您的SP的参数发生变化,则可能会生成执行计划无用且不是最佳执行计划。我认为每次想要使用whit defrence参数计算执行时间时都必须重新编译SP。此外,您可以使用SP中的内部参数,在每次运行SP时由SQL Server自动生成执行计划。例如,使用下面的代码:
ALTER Procedure [dbo].[usp_searchImagestest]
(
@CurrentPage As int=1,
@PageSize As int=10,
@searchType As int=1,
@Activeflag As bit=null,
@searchTerm as nvarchar(200),
@TotalRecords As int OUTPUT
)
As begin
Declare
@ICurrentPage int,
@IPageSize int,
@IsearchType int,
@IActiveflag bit,
@IsearchTerm nvarchar(200),
@ITotalRecords int
Set @ICurrentPage = @CurrentPage
Set @IPageSize = @PageSize
Set @IsearchType = @searchType
Set @IActiveflag = Activeflag
Set @IsearchTerm = searchTerm
Set @ITotalRecords = @TotalRecords
然后使用iternal参数代替SP参数。