SQL查询每隔几天就会放慢速度

时间:2014-01-31 14:49:01

标签: sql sql-server database

我有一些查询已经运行好多年了,但突然间它开始放慢了很多。从几乎瞬间到25秒。 经过一番研究后发现问题是“破损”的指标。重新组织和重建查询使用的四个表的所有索引修复了问题。

好极了,我想完了。但几天之后又出现了同样的问题。重建索引修复了它,然后它每隔几天开始回来。查询减慢=>重建索引。 这些表非常不变,一周内添加了几百个项目,它们有300k的记录。

这可能是什么原因?

编辑:

这是关于这个查询,我不知道如何在不破坏事情的情况下提高效率

ALTER PROCEDURE [dbo].[odat_PricesByDealer]
(
    @dealer_id          int,
    @dealergroup_id     int,
    @startdate          datetime,
    @pricetype_id       int,
    @product_groep      nvarchar(30)
)
AS
    SET NOCOUNT ON

    IF @pricetype_id = 0
        SET @pricetype_id = NULL

    IF @product_groep = '0'
        SET @product_groep = NULL

    SELECT a.price_ID 
        , a.make_id
        , e.make_code_name
        , a.dealergroup_ID
        , c.product_ID
        , c.product_code
        , c.product_department
        , c.product_code_name       
        , c.product_groep
        , c.product_subgroep1
        , c.product_subgroep2
        , c.product_producent
        , b.pricerow_lowerbound
        , b.pricerow_upperbound
        , b.pricerow_value
        , aa.startdate AS startdate
        , a.price_enddate AS enddate
        , d.pricetype_id
        , d.pricetype_name
        , 3 AS price_level
    FROM dbo.tblPrices a WITH (NOLOCK) 
    JOIN udf_PricesByDealer(@dealer_id, @startdate) aa ON a.price_startdate = aa.startdate
        AND a.product_id = aa.product_id
        AND a.pricetype_ID = aa.pricetype_ID
        AND a.make_ID = aa.make_ID
        AND a.dealergroup_ID = aa.dealergroup_ID
        AND a.dealer_ID = aa.dealer_ID
    JOIN dbo.tblPriceRows b WITH (NOLOCK) ON a.price_ID = b.price_ID
    JOIN dbo.tblProducts c WITH (NOLOCK) ON a.product_ID = c.product_ID
    JOIN dbo.tblPriceTypes d WITH (NOLOCK) ON a.pricetype_ID = d.pricetype_ID
    JOIN dbo.tblMakes e WITH (NOLOCK) ON a.make_ID = e.make_ID
    WHERE a.make_ID <> 0 
        AND a.dealergroup_ID = @dealergroup_id
        AND a.dealer_ID = @dealer_id
        AND (d.pricetype_id = @pricetype_id OR @pricetype_id IS NULL)
        AND (c.product_groep = @product_groep OR @product_groep IS NULL)
        AND a.price_authorized = 1
        AND c.product_exclude_from_pricelist = 0

和JOIN中的UDF看起来像这样

ALTER FUNCTION [dbo].[udf_PricesByDealer] 
(      
    @dealer_ID  numeric,
    @startdate  datetime
)
RETURNS TABLE
AS      
RETURN      
(     
    SELECT MAX(a.price_startdate) AS startdate
        , a.make_id
        , a.product_id
        , a.pricetype_id
        , a.dealergroup_id
        , a.dealer_id       
    FROM dbo.tblPrices a WITH (NOLOCK)
    JOIN udf_GetMakeIdForDealer(@dealer_id) b ON a.make_ID = b.make_ID  
    WHERE ( (a.price_startdate <= @startdate AND a.price_enddate >= @startdate) 
            OR 
            (a.price_startdate <= @startdate AND a.price_enddate < a.price_startdate) ) 
        AND a.make_ID <> 0      
        AND a.dealergroup_id <> 0
        AND a.dealer_id = @dealer_ID
    GROUP BY a.make_id
        , a.product_id
        , a.pricetype_id
        , a.dealergroup_id
        , a.dealer_id           
)

和UDF上的另一个连接

ALTER FUNCTION [dbo].[udf_GetMakeIdForDealer]
(
    @dealer_ID numeric
)
RETURNS TABLE
AS
RETURN
(
    SELECT m.make_ID
        , m.make_code_name
    FROM [dbo].[tblMakes] m WITH (NOLOCK)
    WHERE (m.make_ID in  (SELECT make_ID 
                        FROM tblDealerGroupMakes WITH (NOLOCK)
                        WHERE dealergroup_ID = (SELECT dealergroup_ID 
                                                FROM tblDealers WITH (NOLOCK)
                                                WHERE dealer_ID = @dealer_ID))
            OR 
            m.make_ID IN (SELECT make_ID 
                        FROM tblDealerMakes WITH (NOLOCK)
                        WHERE dealer_ID = @dealer_ID AND dealermake_exclude = 0))
        AND m.make_ID NOT IN (SELECT make_ID 
                            FROM tblDealerMakes WITH (NOLOCK)
                            WHERE dealer_ID = @dealer_ID AND dealermake_exclude = 1)
)

2 个答案:

答案 0 :(得分:1)

如果索引中的碎片较少,则可以重新组织索引,如果不是重建索引。如果重建索引解决了您的问题,则相应地在实例上创建索引维护计划以进行正确的索引维护。更新统计信息,尽管如果在相关数据库上启用了自动更新统计信息,则会对其进行处理。

主要方面是调整查询。

答案 1 :(得分:1)

Punter015已发布,您必须调整数据库,例如有一个维护计划。 你不知道是否有必要(我曾经遇到过同样的问题)我做了一些调查,在网上找到了一些样本,最后做了这个程序。

检查是否需要reindex并执行reindex。我每晚都用cron任务(sqlcmd.exe)运行它。不知道这是否是最好的方法,但对我来说效果很好。

CREATE PROCEDURE [dbo].[proc_ReorganizeIndexes]
AS
BEGIN

  DECLARE @table nvarchar(100);
  DECLARE @index_name nvarchar(100);
  DECLARE @ext_frag float;
  DECLARE @int_frag float;
  DECLARE @sql nvarchar(max);
  DECLARE local_index_cursor CURSOR FOR

  SELECT object_name(dt.object_id) Tablename
      ,si.name IndexName
      ,dt.avg_fragmentation_in_percent AS ExternalFragmentation
      ,dt.avg_page_space_used_in_percent AS InternalFragmentation
  FROM
  (
      SELECT object_id,index_id,avg_fragmentation_in_percent,avg_page_space_used_in_percent
      FROM sys.dm_db_index_physical_stats (db_id(DB_NAME()),null,null,null,'DETAILED'
  )
  WHERE index_id <> 0) AS dt 
  INNER JOIN sys.indexes si ON si.object_id=dt.object_id 
         AND si.index_id=dt.index_id AND dt.avg_fragmentation_in_percent>10
         AND dt.avg_page_space_used_in_percent<75 ORDER BY avg_fragmentation_in_percent DESC 

  OPEN local_index_cursor
  FETCH NEXT FROM local_index_cursor
  INTO @table, @index_name, @ext_frag, @int_frag

    WHILE @@FETCH_STATUS = 0
    BEGIN

      PRINT 'Need rebuild: ' + @table+ ' for index ' + @index_name + ' Fragmentation (int/ext): ' + CAST(@int_frag AS varchar(100)) + ' / ' + CAST(@ext_frag AS varchar(100))
    SET @sql = 'ALTER INDEX ALL ON ' + @table + ' REBUILD WITH (FILLFACTOR=90)'
      EXEC sp_executesql @sql

      FETCH NEXT FROM local_index_cursor
      INTO @table, @index_name, @ext_frag, @int_frag
    END

  CLOSE local_index_cursor
  DEALLOCATE local_index_cursor

END