使用内部联接减慢SQL查询执行时间

时间:2016-02-18 07:39:59

标签: sql-server query-performance sqlperformance

我正在使用Microsoft SQL Server企业版(64位)。

我的查询执行时间约为1分钟。

  • Docum表格有xxxxxxxx行
  • Pers表格有xxxxxxx行
  • Permarks表格有xxxxxx行

Docum表上的索引:

enter image description here

Pers表上的索引:

Permarks表上的索引:

PERSMARKS_pm_p_id   
PERSON_MARKScode_AND_date_till_AND_end_date_

查询:

SELECT doc
FROM docum(NOLOCK)
INNER JOIN pers(NOLOCK) ON doc = p
INNER JOIN permarks(NOLOCK) ON pm = p
WHERE doccode IN (20, 21, 22, 23, 24, 25, 30) 
  AND pm_ = 14
  AND (enddate IS NULL OR enddate > getdate())
  AND (date_till IS NULL OR date_till > getdate())

如何加快此查询?

这是完整查询,执行时间是5分钟INTO #temp:

SELECT f
    ,0 AS viso
    ,count(DISTINCT p) AS el_budu
    ,0 AS vidinis
    ,0 AS pasirasyta
    INTO #temp
FROM documents(NOLOCK)
INNER JOIN fo(NOLOCK) ON doc = fv
INNER JOIN for(NOLOCK) ON fve = f
INNER JOIN per(NOLOCK) ON doc = p
INNER JOIN tax ti(NOLOCK) ON p = ti
INNER JOIN permarks(NOLOCK) ON pm = p
WHERE pmtcode = 14
    AND (
        enddate IS NULL
        OR enddate > getdate()
        )
    AND (
        datetill IS NULL
        OR datetill > getdate()
        )
    AND startdate >= '2015-01-01'
    AND enddate <= '2015-12-31'
    AND rtcode = 1
    AND fvcode IN (25)
    AND doccode IN (
        20
        ,21
        ,22
        ,23
        ,24
        ,25
        ,30
        )
GROUP BY fcode

执行计划结果:results photo

2 个答案:

答案 0 :(得分:0)

enter image description here

在上图中,我标记了具有聚簇/非聚簇索引的列。如果上面的图像中写有任何错误,请纠正我。我的观点如下: -

  1. 表文档和person_marks中是否有任何聚簇索引。它们似乎都有非聚集索引。如果没有聚簇索引,那么非聚簇索引必须从表中获取价值昂贵且您无法获得良好的执行计划。

  2. 如果值是唯一的,则在pm_p_id和doc_p_id上创建聚簇索引。这将有助于优化器使用合并连接而不是散列连接。

  3. 使用#temp table

答案 1 :(得分:0)

我为您的可读性重新构建了查询。我还补充道 它们表示在表格中表示的别名 应该是一个很好的习惯,特别是对于任何人 必须跟随/与您合作并实际了解数据 来自于不必要求桌子结构。

您正在加入人员表,但没有真正使用任何东西 除了要加入的“p_id”之外的人的记录 person_marks表。

SELECT 
      d.doc_p_id
   FROM 
      documents d (NOLOCK)
         INNER JOIN persons p (NOLOCK) 
            ON d.doc_p_id = p.p_id
            INNER JOIN person_marks pm (NOLOCK) 
               ON p.p_id = pm.pm_p_id 
              AND pm.pm_pmt_code = 14
              AND (pm.pm_end_date IS NULL OR pm.pm_end_date > getdate())
              AND (pm.pm_date_till IS NULL OR pm.pm_date_till > getdate())
   WHERE 
      d.doc_dprt_code IN (20, 21, 22, 23, 24, 25, 30) 

通过传递过程,如果文档“doc_p_id”是该人 ID,然后可以用来直接转到person_marks表 没有加入人员完全从混合中删除 (除非这只是一个样本,你将抓住个人 以后有关生产查询的信息。)

SELECT 
      d.doc_p_id
   FROM 
      documents d (NOLOCK)
         INNER JOIN person_marks pm (NOLOCK) 
            ON d.doc_p_id = pm.pm_p_id 
           AND pm.pm_pmt_code = 14
           AND (pm.pm_end_date IS NULL OR pm.pm_end_date > getdate())
           AND (pm.pm_date_till IS NULL OR pm.pm_date_till > getdate())
   WHERE 
      d.doc_dprt_code IN (20, 21, 22, 23, 24, 25, 30) 

接下来,对于索引,我建议使用以下表/索引 覆盖查询条件的索引,因此它没有 转到实际数据页以解析条目。

table          index
documents      ( doc_dprt_code, doc_p_id )
person_marks   ( pm_p_id, pm_pmt_code, pm_end_date, pm_date_till )

最后,由于您(目前)只抓取人员ID,您可能想要更改为

select DISTINCT d.doc_p_id ...

因此,每个人只返回一条记录(同样,除非您实际抓取其他数据,只是为了后期/支持目的简化查询)。