降低聚集索引查找成本 SQL Server 2008 R2

Dee*_*k D 4 sql-server-2008 sql-server

我正在运行一个查询,它有数十万条记录,获取数据需要 20 多分钟。运行执行计划后,我注意到聚集索引查找成本可能是原因。如何减少下面提到的查询的聚集索引查找成本?

外键的成本约为 13% 到 23%。

CREATE PROC [dbo].[Test] (@UserTypeID  INT,
                          @UserID      INT,
                          @CityID      INT,
                          @OperatorID  INT,
                          @ParameterID INT)
AS
  BEGIN
      DECLARE @temp TABLE (
        range  DECIMAL(18, 2),
        range2 DECIMAL(18, 2),
        image  VARCHAR(50),
        symbol VARCHAR(20))

      IF( @UserID > 0 )
        BEGIN
            --print 'hii'    
            INSERT INTO @temp
                        (range,
                         range2,
                         image,
                         symbol)
            SELECT tbl_Legend_ViewNetwork_Dtls.range,
                   tbl_Legend_ViewNetwork_Dtls.range2,
                   tbl_Legend_ViewNetwork_Dtls.image,
                   tbl_Legend_ViewNetwork_Dtls.symbol
            FROM   tbl_Legend_ViewNetwork_Dtls
                   INNER JOIN tbl_Legend_ViewNetwork
                     ON tbl_Legend_ViewNetwork_Dtls.tbl_legend_view_network_id = tbl_Legend_ViewNetwork.id
            WHERE  tbl_Legend_ViewNetwork.parameter_id = @ParameterID
                   AND tbl_Legend_ViewNetwork.user_type_id = @UserTypeID
                   AND tbl_Legend_ViewNetwork.is_default = 1
                   AND tbl_Legend_ViewNetwork.user_id = @UserID

            UPDATE @temp
            SET    range = range2,
                   range2 = range
            WHERE  symbol = '<'
        END
      ELSE
        BEGIN
            INSERT INTO @temp
                        (range,
                         range2,
                         image,
                         symbol)
            SELECT tbl_Legend_ViewNetwork_Dtls.range,
                   tbl_Legend_ViewNetwork_Dtls.range2,
                   tbl_Legend_ViewNetwork_Dtls.image,
                   tbl_Legend_ViewNetwork_Dtls.symbol
            FROM   tbl_Legend_ViewNetwork_Dtls
                   INNER JOIN tbl_Legend_ViewNetwork
                     ON tbl_Legend_ViewNetwork_Dtls.tbl_legend_view_network_id = tbl_Legend_ViewNetwork.id
            WHERE  tbl_Legend_ViewNetwork.parameter_id = @ParameterID
                   AND tbl_Legend_ViewNetwork.user_type_id = @UserTypeID
                   AND tbl_Legend_ViewNetwork.is_default = 1

            UPDATE @temp
            SET    range = range2,
                   range2 = range
            WHERE  symbol = '<'
        END

      --select * from @temp          
      SELECT '[' + STUFF((SELECT ',{"latitude":"' + a.lat + '","longitude":"' + a.long + '","value":"' + CONVERT(VARCHAR(20), a.value) + '","image":"' + temp.image + '"}'
                          FROM   (SELECT tbl_Survey_Details.lat,
                                         tbl_Survey_Details.long,
                                         tbl_Survey_Details.value
                                  FROM   tbl_Survey_Details
                                         INNER JOIN tbl_Survey
                                           ON tbl_Survey_Details.tbl_survey_id = tbl_Survey.id
                                         INNER JOIN tbl_Location
                                           ON tbl_Survey.tbl_location_id = tbl_Location.id
                                         INNER JOIN tbl_Area
                                           ON tbl_Location.tbl_area_id = tbl_Area.id
                                         INNER JOIN tbl_City
                                           ON tbl_Area.tbl_city_id = tbl_City.id
                                  WHERE  tbl_Survey_Details.tbl_parameter_id = @ParameterID
                                         AND tbl_Survey.tbl_mobile_operator_id = @OperatorID
                                         AND tbl_Area.tbl_city_id = @CityID) AS a
                                 INNER JOIN @temp temp
                                   ON a.value BETWEEN temp.range AND temp.range2
                          FOR XML Path ('')), 1, 1, '') + ']' AS data
  END 
Run Code Online (Sandbox Code Playgroud)

执行计划截图

修订查询:

CREATE PROC [dbo].[Test] (@UserTypeID  INT,
                          @UserID      INT,
                          @CityID      INT,
                          @OperatorID  INT,
                          @ParameterID INT)
AS
  BEGIN
      DECLARE @temp TABLE (
        range  DECIMAL(18, 2),
        range2 DECIMAL(18, 2),
        image  VARCHAR(50),
        symbol VARCHAR(20))

      IF( @UserID > 0 )
        BEGIN
            --print 'hii'    
            INSERT INTO @temp
                        (range,
                         range2,
                         image,
                         symbol)
            SELECT tbl_Legend_ViewNetwork_Dtls.range,
                   tbl_Legend_ViewNetwork_Dtls.range2,
                   tbl_Legend_ViewNetwork_Dtls.image,
                   tbl_Legend_ViewNetwork_Dtls.symbol
            FROM   tbl_Legend_ViewNetwork_Dtls
                   INNER JOIN tbl_Legend_ViewNetwork
                     ON tbl_Legend_ViewNetwork_Dtls.tbl_legend_view_network_id = tbl_Legend_ViewNetwork.id
            WHERE  tbl_Legend_ViewNetwork.parameter_id = @ParameterID
                   AND tbl_Legend_ViewNetwork.user_type_id = @UserTypeID
                   AND tbl_Legend_ViewNetwork.is_default = 1
                   AND tbl_Legend_ViewNetwork.user_id = @UserID

            UPDATE @temp
            SET    range = range2,
                   range2 = range
            WHERE  symbol = '<'
        END
      ELSE
        BEGIN
            INSERT INTO @temp
                        (range,
                         range2,
                         image,
                         symbol)
            SELECT tbl_Legend_ViewNetwork_Dtls.range,
                   tbl_Legend_ViewNetwork_Dtls.range2,
                   tbl_Legend_ViewNetwork_Dtls.image,
                   tbl_Legend_ViewNetwork_Dtls.symbol
            FROM   tbl_Legend_ViewNetwork_Dtls
                   INNER JOIN tbl_Legend_ViewNetwork
                     ON tbl_Legend_ViewNetwork_Dtls.tbl_legend_view_network_id = tbl_Legend_ViewNetwork.id
            WHERE  tbl_Legend_ViewNetwork.parameter_id = @ParameterID
                   AND tbl_Legend_ViewNetwork.user_type_id = @UserTypeID
                   AND tbl_Legend_ViewNetwork.is_default = 1

            UPDATE @temp
            SET    range = range2,
                   range2 = range
            WHERE  symbol = '<'
        END

   SELECT   a.lat, a.long,a.value, temp.image
                          FROM   (SELECT tbl_Survey_Details.lat,
                                         tbl_Survey_Details.long,
                                         tbl_Survey_Details.value
                                  FROM   tbl_Survey_Details
                                         INNER JOIN tbl_Survey
                                           ON tbl_Survey_Details.tbl_survey_id = tbl_Survey.id
                                         INNER JOIN tbl_Location
                                           ON tbl_Survey.tbl_location_id = tbl_Location.id
                                         INNER JOIN tbl_Area
                                           ON tbl_Location.tbl_area_id = tbl_Area.id
                                         INNER JOIN tbl_City
                                           ON tbl_Area.tbl_city_id = tbl_City.id
                                  WHERE  tbl_Survey_Details.tbl_parameter_id = @ParameterID
                                         AND tbl_Survey.tbl_mobile_operator_id = @OperatorID
                                         AND tbl_Area.tbl_city_id = @CityID) AS a
                                 INNER JOIN @temp temp
                                   ON a.value BETWEEN temp.range AND temp.range2

  END 
Run Code Online (Sandbox Code Playgroud)

修订后的执行计划: 修订后的执行计划

链接到 XML 执行计划文件:单击此处

Aar*_*and 7

由于使用非常简单的查询获取 300,000 行所需的时间大致相同,因此我建议您暂时停止查看查询调整并确定您当前正在测试的特定场景是否现实。

  • 您正在本地工作站上运行 Management Studio,连接到某个 Godaddy 服务器上的 SQL Server 实例。因此,除了 SQL Server 中的查询成本之外,您还受到以下因素的限制:

    • Godaddy 的带宽(与连接到这些服务器的其他人共享)
    • 您的带宽(可能与您家和附近的其他人共享)
    • Management Studio 收集结果,更重要的是渲染结果所需的时间
  • 您正在检索结果中的 300,000 行。通常这不是你做的 - 哪个用户会消耗 300,000 行?考虑在每次拉取时聚合或仅返回一个子集(Google 不会在单个页面中返回 300,000 个结果,他们一次显示 10 个结果),并考虑此查询的实际用途。

由于这不太可能是您的数据库在现实中产生结果的方式,我建议您稍微改变您的测试方法。要么Godaddy 基础设施中的某个服务器上安装 Management Studio ,将带宽和一般 Internet 波动排除在外,要么使用 Management Studio 的本地副本测试您的查询逻辑,但不要使用它来计时结果。相反,使用godaddy 基础设施中的应用程序来测试时间(毕竟,这就是您的应用程序最终将如何工作,对吗?)。

如果在您将带宽/Internet 排除在外时查询也很慢,那么您可以开始考虑从 Godaddy 的服务器获得的 I/O 是否足够(或者您是否真的需要在无论如何,任何时候,所以也许这一点没有实际意义)。