从查询中获取行计数的优化方法包含大量数据

时间:2014-04-26 06:30:59

标签: sql sql-server tsql rowcount

我使用以下查询返回rowcount进行分页,它工作正常但需要很长时间才能返回,因为所有表都有数百万条记录。 目前它需要7秒才能返回rowcount,任何人都可以帮助我快速返回它。

我也尝试过与#table和@table相同的查询都很慢。 查询是

WITH cte_rowcount 
     AS (SELECT p.policyid 
         FROM   resident (nolock) r 
                INNER JOIN resident_policy (nolock) rp 
                        ON r.residentid = rp.residentid 
                INNER JOIN policy (nolock) p 
                        ON p.policyid = rp.policyid 
                --INNER JOIN PolicySource (NOLOCK) psourse ON p.PolicySourceID = psourse.PolicySourceId 
                INNER JOIN policy_locations (nolock) pl 
                        ON pl.policyid = p.policyid 
                INNER JOIN location (nolock) l 
                        ON pl.locationid = l.locationid 
                --INNER JOIN Policy_Status (NOLOCK) ps ON ps.PolicyStatusId = p.PolicyStatusId 
                INNER JOIN property (nolock) pr 
                        ON pr.propertyid = l.propertyid 
         --INNER JOIN dbo.States (NOLOCK) s ON s.StateId = pr.StateId 
         WHERE  r.primary_resident = 0x1 
                AND ( ( @ResidentFirstName IS NULL ) 
                       OR R.firstname LIKE @ResidentFirstName + '%' ) 
                AND ( ( @ResidentLastName IS NULL ) 
                       OR R.firstname LIKE @ResidentLastName + '%' ) 
                AND ( @PropertyAddress IS NULL 
                       OR pr.address LIKE @PropertyAddress + '%' ) 
                AND ( @Policynumber IS NULL 
                       OR p.policynumber LIKE @Policynumber + '%' ) 
                AND ( @LocationAddress IS NULL 
                       OR l.address2 LIKE @LocationAddress + '%' ) 
                AND ( @City IS NULL 
                       OR pr.city LIKE @City + '%' ) 
                AND ( @ZipCode IS NULL 
                       OR pr.zipcode = @ZipCode ) 
                AND ( @StateId IS NULL 
                       OR pr.stateid = @StateId ) 
                AND ( @PolicyStatusId IS NULL 
                       OR p.policystatusid = @PolicyStatusId )) 
SELECT @rowcount = Count(*) 
FROM   cte_rowcount 

3 个答案:

答案 0 :(得分:1)

这很难回答,因为有大量数据可能会发生很多事情。

在加入方面,这应该表现良好。 如果此查询仅用于执行计数,那么我建议您直接SELECT count('x')执行此操作而不使用CTE且不使用(nolock)

SELECT @rowcount = count('x') as rc
FROM   
    resident r 
    INNER JOIN resident_policy rp 
        ON r.residentid = rp.residentid 
    INNER JOIN policy p 
        ON p.policyid = rp.policyid 
    INNER JOIN policy_locations pl 
        ON pl.policyid = p.policyid 
    INNER JOIN location l 
        ON pl.locationid = l.locationid 
    INNER JOIN property pr 
        ON pr.propertyid = l.propertyid 
WHERE  
    r.primary_resident = 0x1 
    AND ( ( @ResidentFirstName IS NULL ) 
        OR R.firstname LIKE @ResidentFirstName + '%' ) 
    AND ( ( @ResidentLastName IS NULL ) 
        OR R.firstname LIKE @ResidentLastName + '%' ) 
    AND ( @PropertyAddress IS NULL 
        OR pr.address LIKE @PropertyAddress + '%' ) 
    AND ( @Policynumber IS NULL 
        OR p.policynumber LIKE @Policynumber + '%' ) 
    AND ( @LocationAddress IS NULL 
        OR l.address2 LIKE @LocationAddress + '%' ) 
    AND ( @City IS NULL 
        OR pr.city LIKE @City + '%' ) 
    AND ( @ZipCode IS NULL 
        OR pr.zipcode = @ZipCode ) 
    AND ( @StateId IS NULL 
        OR pr.stateid = @StateId ) 
    AND ( @PolicyStatusId IS NULL 
        OR p.policystatusid = @PolicyStatusId )

如果此CTE同时用于行计数并从CTE检索数据,请确保您仅检索相关网页的数据(仅包含ROWCOUNT() as RCRC > 0 AND RC <= 20的20个元素)

在数据库方面,您可以检查是否有所有join子句的索引。看起来只有PK,所以他们已经有了索引。请确保您在已连接的列上有索引。

如果您仍然遇到问题,请使用“实时执行计划”功能,看看到底发生了什么。

LIKE条件可以是性能杀手,具体取决于文本大小和数据库内容。您可以考虑使用COLLECTION来存储您的文本,并在文本比较方面获得一些收益。

答案 1 :(得分:1)

我说要查看索引,但它可能不会有多大帮助,因为a)你可能已经做过了,并且b)你不能在这种查询中找到它,只扫描。

想法是摆脱这些OR并允许优化器产生合理的计划。

有两种选择。

不知道哪个版本的SQL Server存在问题,但如果它是SQL 2008 SP1 CU5(10.0.2746)或更高版本,或SQL 2008 R2 CU1(10.50.1702)或更高版本,或者比这更新的东西,在查询中添加option (recompile)。这应该会产生更好的计划,使用相关指数的搜索。

但是,这会为每次执行添加一些重新编译开销,所以第二个选项可能更好。

您可以将查询重写为动态查询,并在优化程序甚至查看查询之前消除NULL参数。我试图重写您的查询,没有您的数据,因此无法对其进行测试,并且可能存在一些错误,但您仍会得到我的意图。我不得不猜测数据类型。 (顺便说一下,有SELECT p.policyid的具体原因吗?)

这是:

declare @qry nvarchar(4000), @prms nvarchar(4000);
set @qry = N'
SELECT count(*)
         FROM   resident (nolock) r 
                INNER JOIN resident_policy (nolock) rp 
                        ON r.residentid = rp.residentid 
                INNER JOIN policy (nolock) p 
                        ON p.policyid = rp.policyid 
                INNER JOIN policy_locations (nolock) pl 
                        ON pl.policyid = p.policyid 
                INNER JOIN location (nolock) l 
                        ON pl.locationid = l.locationid 
                INNER JOIN property (nolock) pr 
                        ON pr.propertyid = l.propertyid 
         WHERE  r.primary_resident = 0x1 '
if @ResidentFirstName IS NOT NULL
    set @qry = @qry + ' AND R.firstname LIKE @ResidentFirstName + ''%'''  
if @ResidentLastName IS NOT NULL 
    set @qry = @qry + ' AND R.firstname LIKE @ResidentLastName + ''%'''
if @PropertyAddress IS NOT NULL 
    set @qry = @qry + ' AND pr.address LIKE @PropertyAddress + ''%''' 
if @Policynumber IS NOT NULL 
    set @qry = @qry + ' AND p.policynumber LIKE @Policynumber + ''%''' 
if @LocationAddress IS NOT NULL 
    set @qry = @qry + ' AND l.address2 LIKE @LocationAddress + ''%''' 
if @City IS NOT NULL 
    set @qry = @qry + ' AND pr.city LIKE @City + ''%''' 
if @ZipCode IS NOT NULL 
    set @qry = @qry + ' AND pr.zipcode = @ZipCode'
if @StateId IS NOT NULL 
    set @qry = @qry + ' AND pr.stateid = @StateId'
if @PolicyStatusId IS NOT NULL 
    set @qry = @qry + ' AND p.policystatusid = @PolicyStatusId'


set @prms = N'@PolicyStatusId int, @StateId int, @ZipCode int,
@City varchar(50), @LocationAddress varchar(50), @Policynumber varchar(50), 
@PropertyAddress varchar(50), @ResidentLastName varchar(50), @ResidentFirstName varchar(50)'

exec sp_executesql 
@qry, 
@prms,
@PolicyStatusId = @PolicyStatusId, @StateId = @StateId, @ZipCode = @ZipCode,
@City = @City, @LocationAddress = @LocationAddress, 
@Policynumber = @Policynumber, @PropertyAddress = @PropertyAddress, 
@ResidentLastName = @ResidentLastName, @ResidentFirstName = @ResidentFirstName

如果您对执行计划有所影响,只要在WHERE和JOIN列上有非聚簇索引,您就会看到索引搜索。

此外,计划将被缓存,每个参数组合一个。

答案 2 :(得分:0)

有一些一般性指示:

  • 在外键列的全部
  • 上创建非聚集索引
  • 在primary_resident列上创建非聚集索引
  • 运行查询时包含实际执行计划,并查看哪个部分是浪费时间
  • 放置最有可能false的语句
  • 当您运行查询时,SQL Server会建议您提供一些提示,也可以尝试使用