【问题标题】:Search stored procedure that returns total records found, is very slow搜索返回找到的总记录的存储过程非常慢
【发布时间】:2012-06-10 22:07:32
【问题描述】:

我有下面的存储过程,它在不返回总记录的情况下工作得很好,但是当我在单独的查询中返回总记录时,它变得很慢。

还有其他方法可以更快地返回总记录

视图是

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)

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 行

【问题讨论】:

  • 在针对视图创建复杂查询时,我在 SQL Server 中遇到了很多问题……尤其是当该视图由其他视图组成时,这些视图通常可以由其他视图组成。我的严肃建议:在存储过程或任何不面向最终用户的场所中,如果可以的话,直接反对表格。
  • @Jeremy : 我尝试直接访问表,查询运行速度非常快,不到 1 秒就返回了计数结果

标签: sql-server sql-server-2008 full-text-search


【解决方案1】:

第二个选择正在重新运行搜索。只需更改您的分页搜索:

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 

线

COUNT(*) OVER() 作为 TOTALCOUNT

在分页发生之前为您提供内部 SELECT 的计数。

【讨论】:

  • 我尝试了您的解决方案,但仍然花费相同的时间,问题在于计数。我在数据库中有 100000 条记录,没有计数的查询不到 1 秒
  • 您是否删除了第二个查询? “选择@TotalRecords”查询?
  • 是的,我删除了它,但是当我不通过视图直接访问表时,即使有第二个查询,结果也会在不到一秒的时间内返回
  • 视图在上面,但即使没有视图,count(*) over() as totalcount 也比获取计数的另一个查询慢
  • @Moe9977:在the article(需要注册才能读取它)中描述了为什么count(*) over()很慢以及如何改进它。我试图在我的回答中简短地描述一下。
【解决方案2】:

我认为,当您使用存储过程获取带参数的查询结果时,SQL Server 会为此 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

然后使用内部参数而不是SP参数。

【讨论】:

  • @lotfi,我会测试一下我很快就会发布结果
  • @Lotfi,使用内部参数仅根据以下影响 CPU 时间:CPU 时间为 46MS 而不是 62,但读取总数相同 2635,因此使用内部参数执行查询更快。
【解决方案3】:

我认为应该改变 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 answerthe article(需要注册到最后一个)。

我个人会以任何方式使用CTE 而不是子查询。通过这种方式,您可以将所有行的主集定义为 CTE。您只需要执行一次获取COUNT(*) 并另外获取您需要的数据页面WHERE seq BETWEEN (@PageSize * (@CurrentPage-1)) AND (@CurrentPage * @PageSize)

另一种选择是使用CROSS JOIN 方法而不是ROW_NUMBER() OVER (&lt;reverse sort order&gt;)(参见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 而不是子查询。

【讨论】:

  • ,你的速度比 iKnowKungFoo 快,但是当我将你的与查询 2 进行比较时,分析器的读取毫秒数更少,CPU 也更低,你给 521 毫秒,上面的单独查询 2 给 61 毫秒
  • @Moe9977:你能写出你测试过的查询吗?我给你写了关于树版本的文章:ROW_NUMBER() OVER (ORDER BY ImgID) AS TotalRows 和 CTE 一个有 CROSS JOIN 和另一个 UNION。在您的问题中,您从问题中写了查询 2“不到 1 秒”。现在你写一些“给出 61 MS”的查询。这怎么能理解?
  • ROW_NUMBER() OVER (ORDER BY ImgID) AS TotalRows 用了 521 毫秒,上面的 Query2 用了 61 毫秒
  • @Moe9977:您应该发布您使用的完整查询。我在回答中包含了我之前描述的更详细的查询。我希望我没有打字错误。你这个和你的结果比较。如果需要,您可以根据我写的参考资料使用UNIONCROSS JOIN。您还可以尝试使用提示 OPTION (RECOMPILE) 并使用不同的输入数据进行测试。有关详细信息,请参阅here
  • 我尝试了 Query2 和您发布的查询,结果如下:查询 2 CPU=63 MS,读取:2635,,您的查询 CPU=1593,读取 229658。但是,我尝试删除ROW_NUMBER() OVER (ORDER BY ImgID) AS TotalRows 改为使用 select @TotalRecords = (select COUNT(*) From vw_MasterImagesSearch WHERE FREETEXT(*, @searchTerm) and (@Activeflag IS NULL or ImgActive=@Activeflag)) 从配置文件读取已更改为 CPU=375 Reads=5078
猜你喜欢
  • 1970-01-01
  • 2014-12-18
  • 1970-01-01
  • 2019-04-20
  • 2012-02-27
  • 2020-01-20
  • 1970-01-01
  • 2017-05-06
  • 1970-01-01
相关资源
最近更新 更多