内部连接的 SQL 查询执行时间慢
Posted
技术标签:
【中文标题】内部连接的 SQL 查询执行时间慢【英文标题】:Slow SQL query execution time with inner joins 【发布时间】:2016-02-18 07:39:59 【问题描述】:我使用的是 Microsoft SQL Server 企业版(64 位)。
我的查询执行时间大约是 1 分钟。
Docum
表有 xxxxxxxx 行
Pers
表有 xxxxxxx 行
Permarks
表有 xxxxxx 行
Docum
表上的索引:
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
【问题讨论】:
doc_dprt_code 中有索引吗? 是的,我有两列 doc_dprt_code 和 doc_reg_date 的非聚集索引 您应该查看execution plan
。顺便说一句,日期过滤器不好。
这很奇怪。然后向您的 DBA 询问计划。
停止破坏你的问题...
【参考方案1】:
在上图中,我用聚集/非聚集索引标记了列。如果上图中写有任何错误,请纠正我。我的观点如下: -
您在表格文档和 person_marks 中是否有任何聚集索引。似乎它们上有所有非聚集索引。如果没有聚集索引,则非聚集索引必须从表中获取值,这很昂贵,并且您将无法获得良好的执行计划。
如果值是唯一的,则尽可能在 pm_p_id 和 doc_p_id 上创建聚集索引。这将有助于优化器使用合并连接而不是哈希连接。
利用#temp表
【讨论】:
1.是的,我在这些表中有聚集索引【参考方案2】:为了便于阅读,我重新组织了您的查询。我还添加了 别名,因为它们出现在您的表中 应该是一个好习惯,尤其是对于那些 必须跟随/与您合作并了解数据的实际位置 来自无需询问表结构。
您正在加入 person 表,但没有真正使用任何东西 除了要加入的“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 ...
因此它每人只返回一条记录(同样,除非您实际上是在获取其他数据并且只是为了发布/支持目的而简化查询)。
【讨论】:
是的,这是一个样本,我需要 person 表来获取 p_legal_count,所有索引都存在以上是关于内部连接的 SQL 查询执行时间慢的主要内容,如果未能解决你的问题,请参考以下文章
mysql数据库: 为啥sql语句在查询分析中的执行速度远远快于在应用程序的(而且有时候后者慢的很多)