SQL 서버 실행 계획을 이해하기

그들이 먼저 용어의 다양한 이제 이해가 어떻게 실행 계획을 이해합니다. 자신의 관점에서 매우 이해가되지 않습니다. 이 문서는 쓰기 만, 이해 이해하지 않는 한 의도 우리는 글을 이해해야한다.

  첫 번째 설명의 시작 부분에서 실행 계획을 볼 수있는 처음 주목해야한다 오른쪽에서 왼쪽으로 SQL 서버의 실행 계획을 볼 수 있습니다.

  기간 분석 :

  스캔 : 프로그레시브 데이터를 통해.

  먼저 테이블을 생성하고, 모든 사람들은 어땠는지보고 아마 위해.

코드를 복사
  TABLE 사람 (CREATE 
      아이디 INT의 IDENTITY (1,1) NOT NULL, 
      이름 NVARCHAR (50) NULL, 
      나이 INT의 NULL, 
      높이 INT의 NULL, 
      지역 NVARCHAR (50) NULL, 
      MarryHistory NVARCHAR (10) NULL, 
      EducationalBackground의 NVARCHAR (10) NULL을 , 
      주소 NVARCHAR (50) NULL, 
      InSiteId INT NULL 
  ) ON [PRIMARY]
코드를 복사

  데이터 테이블 다음과 거의 비슷한 약 14 만은 :

  

  이 테이블은, 어떤 어떤 인덱스가 없다.

데이터 액세스 동작

 1 테이블 스

  표 스캔 : 힙 테이블에 발생 가능한이 테이블 스캔이 발생, 한 번에 전체 테이블 스캔을 나타낼 때 인덱스를 사용할 수 없습니다.

  이제,이 테이블에 대한 간단한 쿼리를 실행하자 :

  사람에서 SELECT * WHERE 이름 = '公子'

  다음과 같이 실행 계획은보기 :

  

  정의에 테이블 스캔, 당신이 필요로하는 데이터를 찾기 위해 전체 테이블 스캔입니다.

 2, 클러스터 된 인덱스 스캔

  집합 인덱스 스캔 표 응집이 발생하고, 전체 테이블 검색 동작에 대응하고 있지만, 이러한 응집 열로서는 조건 (WHERE ID> 10) 등의 작업 효율이 더.

  하자이 열 ID에 클러스터 된 인덱스 테이블을 추가

  CREATE CLUSTERED INDEX IX_Id ON 사람 (ID)

  다시 같은 쿼리를 실행합니다 :

  사람에서 SELECT * WHERE 이름 = '公子'

  다음과 같이 구현 계획은 다음과 같습니다 :

  

  왜 빌드 클러스터 된 인덱스 ID 열 스캔은 영향을 미칠 것인가? 이 아 중요하지 않습니다 이름 조건을 언급하지?

  클러스터 된 인덱스를 추가 한 후 사실, 테이블이 힙 테이블에 의한 수집 테이블된다. 우리는 데이터 집계 테이블이 클러스터 된 인덱스의 리프 노드에 존재하는 것을 알고있다. 따라서, 수집 및 테이블 스캔을 스캔하면 큰 차이를 말하고, 실제로 매우 다르지 않다, 그 반환 된 데이터 후이있는 곳도 어떤 조건에 따라 달라집니다. 이 SQL 문의 목적은, 효율성의 차이는 중요하지 않습니다.

  당신은 I / O 통계를 볼 수 있습니다

  테이블 스캔 :

  

  클러스터 된 인덱스 스캔 :

  

  다음은이 문서의 범위를 넘어, 효율이 논문에서 고려의 범위 내에 있지, 우리는 그 다양한 스캔 구별 고려, 왜 생성합니다.

 3, 클러스터 된 인덱스 탐색

  클러스터 된 인덱스 탐색 : 클러스터 된 인덱스 행의 특정 범위를 스캔.

  다음 SQL 문을 참조하십시오 :

  사람 SELECT * FROM WHERE 아이디 = '73164'

  다음과 같이 구현 계획은 다음과 같습니다 :

  

 (4) 인덱스 스

  인덱스 스캔 : 전체 클러스터되지 않은 인덱스를 스캔.

  의 클러스터 된 인덱스를 추가하고 쿼리를 실행하자 :

  클러스터되지 않은 인덱스 IX_Name ON 사람 (이름이 CREATE -) 클러스터되지 않은 인덱스 만들기 

  사람으로부터 SELECT 이름

  다음과 같이 실행 계획은보기 :

  

  왜 여기에 인덱스 스캔 (비 클러스터형 인덱스) 그것을 선택할 것인가?

  이 클러스터되지 않은 인덱스는 필요한 데이터를 커버하는 때문입니다. 비 클러스터형 인덱스가 그것을 커버 할 수없는 경우? 예를 들어, 우리는 변화 SELECT * SELECT 볼 것이다.

  

   그럼 분명히, 그 결과는 반환에게 너무 많은 기록을 포함하지만, 클러스터되지 않은 인덱스를 지불하지 않습니다. 따라서 클러스터 된 인덱스를 사용하여.

  如果此时我们删除聚集索引,再执行SELECT *看看。

  DROP INDEX Person.IX_Id

  

  而此时没有聚集索引,所以只有使用表扫描。

 5、书签查找

  前面关于索引的学习我们已经知道,当在非聚集索引中并非覆盖和包含所需全部的列时,SQL Server会选择,直接进行聚集索引扫描获得数据,还是先去非聚集索引找到聚集索引键,然后利用聚集索引找到数据。

  下面来看一个书签查找的示例:

  SELECT * FROM Person WHERE Name = '胖胖'  --Name列有非聚集索引

  执行计划如下:

  

  上面的过程可以理解为:首先通过非聚集索引找到所求的行,但这个索引并不包含所有的列,因此还要额外去基本表中找到这些列,因此要进行键查找,如果基本表是以堆进行组织的,那么这个键查找(Key Lookup)就会变成RID查找(RID Lookup),键查找和RID查找统称为书签查找。不过有时当非聚集索引返回的行数过多时,SQL Server可能会选择直接进行聚集索引扫描了。

二、流聚合操作

 1、流聚合

  流聚合:在相应排序的流中,计算多组行的汇总值。

  所有的聚合函数(如COUNT(),MAX())都会有流聚合的出现,但是其不会消耗IO,只有消耗CPU。

   例如执行以下语句:

  SELECT MAX(Age) FROM Person

  查看执行计划如下:

  

 2、计算标量

  计算标量:根据行中的现有值计算新值。比如COUNT()函数,多一行,行数就加1咯。

  除MIN和MAX函数之外的聚合函数都要求流聚合操作后面跟一个计算标量。

  SELECT COUNT(*) FROM Person

  查看执行计划如下:

  

3、散列聚合(哈希匹配)

  对于加了Group by的子句,因为需要数据按照group by 后面的列有序,就需要Sort来保证排序。注意,Sort操作是占用内存的操作,当内存不足时还会去占用tempdb。SQL Server总是会在Sort操作和散列匹配中选择成本最低的

  SELECT Height,COUNT(Id) FROM Person    --查出各身高的认输
  GROUP BY Height

  执行计划如下:

  

  对于数据量比较大时,SQL Server选择的是哈希匹配。

  在内存中建立好散列表后,会按照group by后面的值作为键,然后依次处理集合中的每条数据,当键在散列表中不存在时,向散列表添加条目,当键已经在散列表中存在时,按照规则(规则是聚合函数,比如Sum,avg什么的)计算散列表中的值(Value)。

 4、排序

   当数据量比价少时,例如执行以下语句,新建一个只有数十条记录的与Person一样的表。

  SELECT * INTO Person2 FROM Person2
  WHERE Id < 100

  再来执行同样的查询语句:

  SELECT Height,COUNT(Id) FROM Person2    --只是表换成了数据量比较少的表
  GROUP BY Height

  执行计划如下:

  

三、连接

  当多表连接时(包括书签查找,索引之间的连接),SQL Server会采用三类不同的连接方式:循环嵌套连接,合并连接,散列连接。这几种连接格式有适合自己的场景,不存在哪个更好的说法。

   新建两张表如下

   

   这是一个简单的新闻,栏目结构。

 1、嵌套循环

  先来看一个简单的Inner Join查询语句

  SELECT * FROM Nx_Column AS C
  INNER JOIN Nx_Article AS A
  ON A.ColumnId = C.ColumnId

  执行计划如下:

  

  循环嵌套连接的图标同样十分形象,处在上面的外部输入(Outer input),这里也就是聚集索引扫描。和处在下面的内部输入(Inner Input),这里也就是聚集索引查找。外部输入仅仅执行一次,根据外部输入满足Join条件的每一行,对内部输入进行查找。这里由于是7行,对于内部输入执行7次。

   

  根据嵌套循环的原理不难看出,由于外部输入是扫描,内部输入是查找,当两个Join的表外部输入结果集比较小,而内部输入所查找的表非常大时,查询优化器更倾向于选择循环嵌套方式。

 2、合并连接

  不同于循环嵌套的是,合并连接是从每个表仅仅执行一次访问。从这个原理来看,合并连接要比循环嵌套要快了不少。

  从合并连接的原理不难想象,首先合并连接需要双方有序.并且要求Join的条件为等于号。因为两个输入条件已经有序,所以从每一个输入集合中取一行进行比较,相等的返回,不相等的舍弃,从这里也不难看出Merge join为什么只允许Join后面是等于号。从图11的图标中我们可以看出这个原理。

  SELECT * FROM Nx_Column AS C
  INNER JOIN    Nx_Article AS A
  ON A.ColumnId = C.ColumnId
  OPTION(MERGE join)

  执行计划如下:

  

  如果输入数据的双方无序,则查询分析器不会选择合并连接,我们也可以通过索引提示强制使用合并连接,为了达到这一目的,执行计划必须加上一个排序步骤来实现有序。这也是上述SQL语句为什么要加OPTION(MERGE join)的原因。上述对Article表的ColumnId列进行了排序。

 3、哈希连接

  散列连接同样仅仅只需要只访问1次双方的数据。散列连接通过在内存中建立散列表实现。这比较消耗内存,如果内存不足还会占用tempdb。但并不像合并连接那样需要双方有序。

  要进行下面这两个实现,得把两个列的聚集索引不要建在ColumnId列,否则不会采用哈希连接。

  ALTER TABLE PK_Nx_Column DROP CONSTRAINT PK_Nx_Column    --删除主键
  DROP INDEX Nx_Column.PK_Nx_Column    --删除聚集索引
  CREATE CLUSTERED INDEX IX_ColumnName ON Nx_Column(ColumnName)    --创建聚集索引
  --这里再设置回主键就可以了,有了聚集索引,就不能随主键默认建啦

  还要删除另外一个表Article的聚集索引哦。

  然后执行以下查询:

  SELECT * FROM Nx_Column AS C
  INNER JOIN    Nx_Article AS A
  ON A.ColumnId = C.ColumnId

  执行计划如下:

  

    要删除掉聚集索引,否则两个有序输入SQL Server会选择代价更低的合并连接。SQL Server利用两个上面的输入生成哈希表,下面的输入来探测,可以在属性窗口看到这些信息,如图15所示。

    일반적으로, 하나 또는 둘 모두의 요청에 따라 데이터를 해시 일치를 선택합니다 충족 조건의 종류, 아니다.

넷째, 병렬

  복수의 테이블을 연결할 때, SQL 서버도 허용 의심 효율 향상, 멀티 CPU 또는 다핵 동시 쿼리를 허용한다.

 

저자 : 고양이 치
원본 소스 : http://qixuejia.cnblogs.com/

 

추천

출처www.cnblogs.com/ztf20/p/12052274.html