Избавляемся от полного сканирования индекса - PullRequest
2 голосов
/ 06 июля 2010

Следующий запрос работает плохо из-за полного сканирования некластеризованного индекса в 6,5 миллиона записей в выпусках P4FileReleases с последующим хеш-соединением. Я ищу возможные причины, по которым оптимизатор сканирует поисковый запрос.

SELECT p4f.FileReleaseID 
   FROM P4FileReleases p4f
   INNER JOIN AnalyzedFileView af 
      ON p4f.FileRelease = (af.path+'#'+cast(af.revision as varchar))  
   WHERE (af.tracked_change_id = 1)

Из того, что я могу сказать, я не вижу причин для оптимизатора выбирать сканирование P4FileReleases. Предложение WHERE ограничивает размер правого набора данных до 1 КБ записей, и оптимизатор должен это знать (см. Гистограмму ниже).

Если факт, если я возьму данные представления и выброслю их в таблицу кучи (та же структура, что и в индексированном представлении), то запрос выполняется с поиском индекса по большей таблице и внутренним циклом соединения вместо хеша. присоединиться (и общая стоимость снизится с 145 до 1).

Любые идеи о том, что может скинуть оптимизатор?

Подробности. Sql Server 2008 (v. 10.0.2757.0).

Таблица P4FileReleases Держит 6,5 миллиона записей

CREATE TABLE [dbo].[P4FileReleases](
    [FileReleaseID] [int] IDENTITY(1,1) NOT NULL,
    [FileRelease] [varchar](254) NOT NULL,
    -- 5 more fields 
 CONSTRAINT [CIX_P4FileReleases_FileReleaseID_PK] PRIMARY KEY CLUSTERED 
(
    [FileReleaseID] ASC
),
CONSTRAINT [NCIX_P4FileReleases_FileRelease] UNIQUE NONCLUSTERED 
(
    [FileRelease] ASC
)

AnalyzedFileView является индексированным представлением с включенной и актуальной статистикой.

Имеет четыре столбца:

   key int (int, PK) - clustered index
   tracked_change_id (int, FK) - non-unique, non-clustered index (covering 'path', 'revision')
   path (nvarchar(1024), null) 
   revision (smallint, null)

гистограмма tracked_change_id:

1   0   1222    0   1
4   0   787     0   1
8   0   2754    0   1
12  0   254     0   1
13  0   34      0   1

План запроса

  |--Parallelism(Gather Streams)
       |--Hash Match(Inner Join, HASH:([Expr1011])=([Expr1010]), RESIDUAL:([Expr1010]=[Expr1011]))
            |--Bitmap(HASH:([Expr1011]), DEFINE:([Bitmap1015]))
            |    |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([Expr1011]))
            |         |--Compute Scalar(DEFINE:([Expr1011]=([qpsitools].[dbo].[analyzed_file_view].[path]+N'#')+CONVERT_IMPLICIT(nvarchar(30),CONVERT(varchar(30),[qpsitools].[dbo].[analyzed_file_view].[revision],0),0)))
            |              |--Index Seek(OBJECT:([qpsitools].[dbo].[analyzed_file_view].[tracked_change_id]), SEEK:([qpsitools].[dbo].[analyzed_file_view].[tracked_change_id]=(1)) ORDERED FORWARD)
            |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([Expr1010]), WHERE:(PROBE([Bitmap1015],[Expr1010])))
                 |--Compute Scalar(DEFINE:([Expr1010]=CONVERT_IMPLICIT(nvarchar(254),[Blueprint].[dbo].[P4FileReleases].[FileRelease] as [p4f].[FileRelease],0)))
                      |--Index Scan(OBJECT:([Blueprint].[dbo].[P4FileReleases].[NCIX_P4FileReleases_FileRelease] AS [p4f]))

Ответы [ 4 ]

5 голосов
/ 06 июля 2010

Вы присоединяетесь к столбцу varchar p4f.FileRelease со столбцом nvarchar (af.path).Поскольку типы данных не совпадают, SQL должен преобразовывать один тип в другой (и, конечно, он не может перейти от nvarchar к varchar).При преобразовании af.path в nvarchar он теряет возможность использовать индекс для поиска / фильтрации этих значений, что приводит к необходимости сканирования и преобразования всех возможных строк.соответствующие типы данных (измените столбец p4f.FileRelase на nvarchar или af.path на varchar).Поскольку никто и никогда не сможет изменить существующие структуры базы данных, временным решением может быть явное приведение af.path к varchar в запросе.Проверьте это и посмотрите ... хотя, конечно, вы не можете сделать это, если данные действительно требуют двухбайтового форматирования.

2 голосов
/ 06 июля 2010

ваша проблема не в WHERE, а в JOIN, вы получаете неявное преобразование и сканирование JOIN, при условии, что WHERE вы получаете SEEK

ON p4f.FileRelease = (af.path+'#'+cast(af.revision as varchar))  

Параллелизм также может быть проблемойпопробуйте добавить MAXDOP = 1

Актуальна ли ваша статистика?Есть ли чрезмерная фрагментация?

0 голосов
/ 06 июля 2010

Филипп Келли заметил проблему.Это было несовпадение типов данных между varchar в P4FileReleases и nvarchar в AnalyzedFileView.

0 голосов
/ 06 июля 2010

Попробуйте переместить «af.tracked_change_id = 1» в предложение объединения.

INNER JOIN AnalyzedFileView af 
ON p4f.FileRelease = (af.path+'#'+cast(af.revision as varchar))
AND af.tracked_change_id = 1

ГДЕ применяется после ВНУТРЕННЕГО СОЕДИНЕНИЯ

...