优化长时间运行的SQL Server查询

时间:2012-12-05 11:29:12

标签: sql sql-server performance query-optimization

我有以下查询:

SELECT fpa.scenario_id,
   fpa.facility_id,
   cge.CostGroupId result_total_id,
   mp_surrogate_id,
   CAST(SUM(fpa.raw_amount * cge.CostSign) AS DECIMAL(25, 13)) 
   result_total_amount         
INTO ADM_FactProfitTotalAmount_1
FROM #tempAmount fpa
JOIN ResultTest cge ON cge.CostId = fpa.process_id 
WHERE fpa.scenario_id = 1
GROUP BY fpa.scenario_id, fpa.facility_id, cge.CostGroupId, fpa.mp_surrogate_id
  • #tempAmount我有2.2亿行。
  • ResultTest我有150行。

我有#tempAmount的索引:

CREATE NONCLUSTERED INDEX #tempAmount_process_id
ON  #tempAmount(scenario_id, facility_id, mp_surrogate_id, process_id )

执行大约需要1个小时。是否有可能对其进行优化?

编辑:

我在ResultTest列CostId上创建了索引,更改了一些其他索引和查询

    CREATE CLUSTERED INDEX #tempFactAmount_index 
    ON  #tempAmount (process_id ,facility_id, mp_surrogate_id )

    SELECT  ISNULL(CAST(1 as BIGINT), 0) scenario_id,
            fpa.facility_id,
            cge.CostGroupId result_total_id,
            fpa.mp_surrogate_id,
            CAST(SUM(fpa.raw_amount * cge.CostSign) AS DECIMAL(25, 13)) result_total_amount         
    INTO ADM_FactProfitTotalAmount_1
    FROM ResultTest cge
    JOIN #tempAmount fpa ON cge.CostId = fpa.process_id 
    GROUP BY fpa.facility_id, fpa.mp_surrogate_id, cge.CostGroupId

执行计划:

41%插入ADM_FactProfitTotalAmount_1

51%哈希匹配聚合

2%哈希匹配内部联接

3 个答案:

答案 0 :(得分:2)

在这样的场景中,我发现在加入较小的表之前对较大表中的金额求和通常会有所帮助。所以在这种情况下我会使用以下内容:

;WITH SUMCTE
AS
(
SELECT      fpa.facility_id,
            fpa.mp_surrogate_id,
            fpa.process_id,
            SUM(fpa.raw_amount) AS total_amount         
    FROM #tempAmount fpa 
    GROUP BY fpa.facility_id, fpa.mp_surrogate_id, fpa.process_id
)
SELECT  CAST(1 as BIGINT) AS Scenario_id,
        facility_id,
        cge.CostGroupId result_total_id,
        mp_surrogate_id,
        CAST(SUM(SCT.total_amount * cge.CostSign) AS DECIMAL(25, 13)) result_total_amount         
    INTO ADM_FactProfitTotalAmount_1
    FROM ResultTest cge
    JOIN SUMCTE SCT ON cge.CostId = SCT.process_id 
    GROUP BY fpa.facility_id, fpa.mp_surrogate_id, cge.CostGroupId

如果每个process_id在ResulTest中只有一行,我会通过删除外部组来进一步简化:

;WITH SUMCTE
AS
(
SELECT      fpa.facility_id,
            fpa.mp_surrogate_id,
            fpa.process_id,
            SUM(fpa.raw_amount) AS total_amount         
    FROM #tempAmount fpa 
    GROUP BY fpa.facility_id, fpa.mp_surrogate_id, fpa.process_id
)
SELECT  CAST(1 as BIGINT) AS Scenario_id,
        facility_id,
        cge.CostGroupId result_total_id,
        mp_surrogate_id,
        CAST((SCT.total_amount * cge.CostSign) AS DECIMAL(25, 13)) result_total_amount         
    INTO ADM_FactProfitTotalAmount_1
    FROM ResultTest cge
    JOIN SUMCTE SCT ON cge.CostId = SCT.process_id 

答案 1 :(得分:1)

  • 我建议从检查预计执行计划开始 http://msdn.microsoft.com/en-us/library/ms191194.aspx

  • 多列索引只有在前缀为前缀时才能使用。 http://dev.mysql.com/doc/refman/5.0/en/multiple-column-indexes.html

    所以我建议在scenario_id旁边移动process_id,因为它们用在where和join中。

    创建NONCLUSTERED INDEX #tempAmount_process_id   ON #tempAmount(scenario_id,process_id,facility_id,mp_surrogate_id)

  • 最后一个:让OS尽可能地将磁盘块缓存到内存中。 在linux之前,一些性能关键数据库投入生产, 做“cat your_database.store.file> / dev / null”。 将从内存缓存中点击大量磁盘读取。

答案 2 :(得分:1)

首先,我建议捕获实际的执行计划。如果从SQL Server Management Studio(SSMS)运行查询,请启用“包括实际执行计划”选项。如果此查询从另一个程序运行,请运行SQL Server Profiler并打开Showplan Statistics Profile和/或Showplan XML Statistics Profile。查看此配置文件,查看查询是否符合您的预期。

你有关于ResultTest colum CostId的索引吗?只有150行,此表上的索引扫描不是什么大问题。如果您在此表上没有索引,则可以尝试使用它。

我想知道执行计划是否正在执行嵌套循环以加入ResultTest。如果是这样,那将是150 X 220,000,000 = 330亿次操作。如果是这种情况,散列连接或合并连接将表现得更好。您可以使用联接提示OPTION (HASH JOIN)OPTION (MERGE JOIN)强制执行特定联接。仅这一点就可以产生巨大的差异。

#tempAmount上的索引包含许多SELECT查询不需要的列。此外,它是NONCLUSTERED索引。是否还有一个CLUSTERED指数?如果没有,您可以尝试将其转换为CLUSTERED并删除其他列。这将减小索引的大小并且应该执行得更好,因为scenario_id的所有行都是连续的。