where 절이`value ()`를 필터링 할 때 2 차 선택적 색인이 사용되지 않는 이유는 무엇입니까?


13

설정:

create table dbo.T
(
  ID int identity primary key,
  XMLDoc xml not null
);

insert into dbo.T(XMLDoc)
select (
       select N.Number
       for xml path(''), type
       )
from (
     select top(10000) row_number() over(order by (select null)) as Number
     from sys.columns as c1, sys.columns as c2
     ) as N;

각 행의 샘플 XML :

<Number>314</Number>

쿼리의 작업 T은 지정된 값을 가진 행 수를 계산하는 것 입니다 <Number>.

이를 수행하는 두 가지 확실한 방법이 있습니다.

select count(*)
from dbo.T as T
where T.XMLDoc.value('/Number[1]', 'int') = 314;

select count(*)
from dbo.T as T
where T.XMLDoc.exist('/Number[. eq 314]') = 1;

그것은이 밝혀 value()exists()작업에 선택적 XML 인덱스에 대한 두 개의 서로 다른 경로 정의를 필요로한다.

create selective xml index SIX_T on dbo.T(XMLDoc) for
(
  pathSQL = '/Number' as sql int singleton,
  pathXQUERY = '/Number' as xquery 'xs:double' singleton
);

sql버전입니다 value()xquery버전입니다 exist().

이와 같은 색인을 사용하면 탐색이 잘되는 계획을 제공 할 수 있지만 선택적 XML 색인은 기본 키가 T시스템 테이블의 클러스터 키의 리드 키인 시스템 테이블로 구현됩니다 . 지정된 경로는 해당 테이블의 스파 스 열입니다. 정의 된 경로의 실제 값에 대한 색인을 원할 경우 각 경로 표현식마다 하나씩 보조 선택 색인을 작성해야합니다.

create xml index SIX_T_pathSQL on dbo.T(XMLDoc)
  using xml index SIX_T for (pathSQL);

create xml index SIX_T_pathXQUERY on dbo.T(XMLDoc)
  using xml index SIX_T for (pathXQUERY);

에 대한 쿼리 계획 exist()은 선택적인 XML 인덱스에 대한 시스템 테이블에서 키 조회 (2 차 XML 인덱스에서 검색)를 수행하고 (필요한 이유를 모름) 최종적으로 T실제로 있는지 확인하기 위해 검색을 수행 합니다. 거기에 행. 시스템 테이블과 사이에 외래 키 제약 조건이 없으므로 마지막 부분이 필요합니다 T.

여기에 이미지 설명을 입력하십시오

value()쿼리 계획 이 그리 좋지 않습니다. T내부 테이블에서 찾기에 대해 중첩 루프 조인 으로 클러스터 된 인덱스 스캔을 수행 하여 스파 스 열에서 값을 가져오고 마지막으로 값을 필터링합니다.

여기에 이미지 설명을 입력하십시오

선택적 인덱스의 사용 여부는 최적화 전에 결정되지만 2 차 선택적 인덱스의 사용 여부는 옵티마이 저의 비용 기반 결정입니다.

where 절이 필터링 될 때 2 차 선택적 색인이 사용되지 않는 이유는 무엇 value()입니까?

최신 정보:

쿼리는 의미 적으로 다릅니다. 값이있는 행을 추가하면

<Number>313</Number>
<Number>314</Number>` 

exist()버전은 2 개 행을 계산 것와 values()쿼리가 1 개 행을 계산한다. 그러나 singleton지시문을 사용하여 여기에 지정된 인덱스 정의를 사용하면 SQL Server에서 여러 <Number>요소가 있는 행을 추가 할 수 없습니다 .

그러나 컴파일러가 단일 값만 얻도록 보장하기 위해 values()지정하지 않고 함수를 사용할 수는 없습니다 [1]. 이것이 [1]우리가 value()계획 에서 Top N Sort를 갖는 이유 입니다.

내가 대답을 마치고있는 것 같습니다 ...

답변:


11

singleton인덱스의 경로 표현식에 선언 하면 여러 <Number>요소를 추가 할 수 없지만 XQuery 컴파일러는 value()함수 의 표현식을 해석 할 때이를 고려하지 않습니다 . [1]SQL Server를 행복하게 하려면 지정해야 합니다. 스키마와 함께 유형이 지정된 XML을 사용해도 도움이되지 않습니다. 그리고 그 때문에 SQL Server는 "적용"패턴이라고 할 수있는 것을 사용하는 쿼리를 작성합니다.

가장 쉬운 방법은 실제로 실행하는 쿼리 T와 내부 테이블을 시뮬레이션하는 XML 대신 일반 테이블을 사용하는 것입니다 .

내부 테이블의 설정은 실제 테이블입니다.

create table dbo.xml_sxi_table
(
  pk1 int not null,
  row_id int,
  path_1_id varbinary(900),
  pathSQL_1_sql_value int,
  pathXQUERY_2_value float
);

go

create clustered index SIX_T on xml_sxi_table(pk1, row_id);
create nonclustered index SIX_pathSQL on xml_sxi_table(pathSQL_1_sql_value) where path_1_id is not null;
create nonclustered index SIX_T_pathXQUERY on xml_sxi_table(pathXQUERY_2_value) where path_1_id is not null;

go

insert into dbo.xml_sxi_table(pk1, row_id, path_1_id, pathSQL_1_sql_value, pathXQUERY_2_value)
select T.ID, 1, T.ID, T.ID, T.ID
from dbo.T;

두 테이블을 모두 갖추면 동등한 exist()쿼리를 실행할 수 있습니다.

select count(*)
from dbo.T
where exists (
             select *
             from dbo.xml_sxi_table as S
             where S.pk1 = T.ID and
                   S.pathXQUERY_2_value = 314 and
                   S.path_1_id is not null
             );

해당 value()쿼리는 다음과 같습니다.

select count(*)
from dbo.T
where (
      select top(1) S.pathSQL_1_sql_value
      from dbo.xml_sxi_table as S
      where S.pk1 = T.ID and
            S.path_1_id is not null
      order by S.path_1_id
      ) = 314;

top(1)order by S.path_1_id범죄자이며 인 [1]탓되는 XPath 표현이다.

함수 [1]에서 제외해도 Microsoft가 내부 테이블의 현재 구조 로이 문제를 해결할 수 있다고 생각하지 않습니다 values(). 옵티마이 저가 <number>각 행에 대해 하나의 요소 만 존재할 수 있도록 고유 제한 조건이있는 각 경로 표현식에 대해 여러 개의 내부 테이블을 작성해야합니다 . 실제로 옵티마이 저가 "적용 패턴을 벗어나기"에 충분한 지 확실하지 않습니다.

이 재미 있고 재미 있다고 생각하고 아직도 읽고 있기 때문에 당신은 아마 당신입니다.

내부 테이블의 구조를 살펴보기위한 일부 쿼리

select T.name, 
       T.internal_type_desc, 
       object_name(T.parent_id) as parent_table_name
from sys.internal_tables as T
where T.parent_id = object_id('T');

select C.name as column_name, 
       C.column_id,
       T.name as type_name,
       C.max_length,
       C.is_sparse,
       C.is_nullable
from sys.columns as C
  inner join sys.types as T
    on C.user_type_id = T.user_type_id
where C.object_id in (
                     select T.object_id 
                     from sys.internal_tables as T 
                     where T.parent_id = object_id('T')
                     )
order by C.column_id;

select I.name as index_name,
       I.type_desc,
       I.is_unique,
       I.filter_definition,
       IC.key_ordinal,
       C.name as column_name, 
       C.column_id,
       T.name as type_name,
       C.max_length,
       I.is_unique,
       I.is_unique_constraint
from sys.indexes as I
  inner join sys.index_columns as IC
    on I.object_id = IC.object_id and
       I.index_id = IC.index_id
  inner join sys.columns as C
    on IC.column_id = C.column_id and
       IC.object_id = C.object_id
  inner join sys.types as T
    on C.user_type_id = T.user_type_id
where I.object_id in (
                     select T.object_id 
                     from sys.internal_tables as T 
                     where T.parent_id = object_id('T')
                     );
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.