Время запроса с JOIN с использованием подзапроса вместо строки - PullRequest
0 голосов
/ 27 марта 2011

У меня такой запрос:

SELECT * FROM table1 t1
JOIN table2 t2 ON t1.id = t2.id 
     AND t2.time IN (SELECT year FROM activeYears WHERE active = 1)

Если activeYears.year - это NVARCHAR (50), то каждый год подряд.

Почему время выполнения этого соединения быстрее, чем этот запрос:

SELECT * FROM table1 t1
JOIN table2 t2 ON t1.id = t2.id AND t2.time IN ('2009','2010')

Это короткая, простая версия, в основном у меня большой запрос с объединением, который использует подзапрос. Когда я изменяю подзапрос на строку для тестирования, он занимает вдвое больше времени, даже при очистке кэша. Я думаю, что это может быть проблемой приведения, но я попытался объявить две переменные как NVARCHAR (50) и использовать их в запросе, и это не имело никакого значения.

Это всего лишь несколько дней меня озадачивало, и я не понимаю, почему подзапрос работает быстрее, если запрос фактически не построен по-другому.

Спасибо!


edit - информация о планах exec

Я разобрал два плана выполнения и постараюсь дать вам анонимные данные.

Раздел MissingIndexes плана выполнения для более быстрого (подзапроса) запроса:

         <QueryPlan CachedPlanSize="196" CompileTime="2166" CompileCPU="2166" CompileMemory="18640">
            <MissingIndexes>
             <MissingIndexGroup Impact="41.4663">
                <MissingIndex Database="[database]" Schema="[dbo]" Table="[table2]">
                 <ColumnGroup Usage="EQUALITY">
                   <Column Name="[time]" ColumnId="3" />
                    <Column Name="[id]" ColumnId="10" />
                  </ColumnGroup>
                </MissingIndex>
              </MissingIndexGroup>
            </MissingIndexes>

Раздел MissingIndexes запроса с использованием строки для t2.time IN ('2009', '2010')

           <MissingIndexes>
              <MissingIndexGroup Impact="35.4994">
                <MissingIndex Database="[database]" Schema="[dbo]" Table="[table2]">
                  <ColumnGroup Usage="INEQUALITY">
                    <Column Name="[time]" ColumnId="3" />
                  </ColumnGroup>
                  <ColumnGroup Usage="INCLUDE">
                    <Column Name="[id]" ColumnId="10" />
                    <Column Name="[field1]" ColumnId="15" />
                  </ColumnGroup>
                </MissingIndex>
              </MissingIndexGroup>
              <MissingIndexGroup Impact="44.364">
                <MissingIndex Database="[database]" Schema="[dbo]" Table="[table2]">
                  <ColumnGroup Usage="EQUALITY">
                    <Column Name="[time]" ColumnId="3" />
                    <Column Name="[id]" ColumnId="10" />
                  </ColumnGroup>
                  <ColumnGroup Usage="INCLUDE">
                    <Column Name="[field1]" ColumnId="15" />
                  </ColumnGroup>
                </MissingIndex>
              </MissingIndexGroup>
            </MissingIndexes>

И затем есть вложенный цикл, который строится по-разному в каждом плане, это резко измененный вложенный цикл в том же порядке, что и выше, первый подзапрос, затем строковая версия:

                <RelOp AvgRowSize="878" EstimateCPU="4.45867E-06" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1.06667" LogicalOp="Left Outer Join" NodeId="1" Parallel="false" PhysicalOp="Nested Loops" EstimatedTotalSubtreeCost="4.44321">
                  <OutputList>
                     --SNIPPED--
                  </OutputList>
                  <NestedLoops Optimized="false">
                    <OuterReferences>
                      <ColumnReference Database="[database]" Schema="[dbo]" Table="[table1]" Alias="[t1]" Column="time" />
                      <ColumnReference Database="[database]" Schema="[dbo]" Table="[table1]" Alias="[t1]" Column="id" />
                    </OuterReferences>

Тот же вложенный цикл, но из второго запроса:

                <RelOp AvgRowSize="878" EstimateCPU="0.223308" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1.06667" LogicalOp="Left Outer Join" NodeId="1" Parallel="false" PhysicalOp="Nested Loops" EstimatedTotalSubtreeCost="4.66781">
                  <OutputList>
                    -- SNIPPED --
                  </OutputList>
                  <NestedLoops Optimized="false">
                    <Predicate>
                      <ScalarOperator ScalarString="[database].[dbo].[table1].[time] as [t1].[time]=[database].[dbo].[table2].[time] as [t2].[time] AND [database].[dbo].[table1].[id] as [t1].[id]=[database].[dbo].[table2].[id] as [t2].[id]">
                        <Logical Operation="AND">
                          <ScalarOperator>
                            <Compare CompareOp="EQ">
                              <ScalarOperator>
                                <Identifier>
                                  <ColumnReference Database="[database]" Schema="[dbo]" Table="[table1]" Alias="[t1]" Column="time" />
                                </Identifier>
                              </ScalarOperator>
                              <ScalarOperator>
                                <Identifier>
                                  <ColumnReference Database="[database]" Schema="[dbo]" Table="[table2]" Alias="[t2]" Column="time" />
                                </Identifier>
                              </ScalarOperator>
                            </Compare>
                          </ScalarOperator>
                          <ScalarOperator>
                            <Compare CompareOp="EQ">
                              <ScalarOperator>
                                <Identifier>
                                  <ColumnReference Database="[database]" Schema="[dbo]" Table="[table1]" Alias="[t1]" Column="id" />
                                </Identifier>
                              </ScalarOperator>
                              <ScalarOperator>
                                <Identifier>
                                  <ColumnReference Database="[database]" Schema="[dbo]" Table="[table2]" Alias="[t2]" Column="id" />
                                </Identifier>
                              </ScalarOperator>
                            </Compare>
                          </ScalarOperator>
                        </Logical>
                      </ScalarOperator>
                    </Predicate>

И, наконец, в том же порядке, есть фрагмент другого, другого вложенного цикла, который выглядит так, как будто он может иметь значение, потому что он, похоже, показывает, как строится этот раздел запроса, сначала версия плана подзапроса:

<ScalarOperator ScalarString="[database].[dbo].[table1].[id] as [t1].[id]=[database].[dbo].[table2].[id] as [t2].[id] AND [database].[dbo].[table1].[time] as [t1].[time]=[database].[dbo].[table2].[time] as [t2].[time] AND [database].[dbo].[table2].[time] as [t2].[time]&gt;=N'2009' AND [database].[dbo].[table2].[time] as [t2].[time]&lt;=N'2010'"> 

Затем строковая версия, IN ('2009', '2010'):

<ScalarOperator ScalarString="[database].[dbo].[table2].[time] as [t2].[time]=N'2009' OR [database].[dbo].[table2].[time] as [t2].[time]=N'2010'">

2-е редактирование - статистическая информация

Для запроса, здесь SET STATISTICS TIME ON и SET STATISTICS IO ON, в том же порядке, что и выше, сначала подзапрос:

Table 'activeYear'. Scan count 2, logical reads 2010, physical reads 2, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table2'. Scan count 1, logical reads 2339848, physical reads 0, read-ahead reads 2303, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table3'. Scan count 1016, logical reads 4624, physical reads 21, read-ahead reads 1047, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 12, logical reads 109, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table4'. Scan count 1, logical reads 126, physical reads 0, read-ahead reads 126, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table1'. Scan count 1033, logical reads 5331, physical reads 57, read-ahead reads 123, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table5'. Scan count 1, logical reads 219, physical reads 0, read-ahead reads 219, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table6'. Scan count 1, logical reads 2, physical reads 2, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
   CPU time = 10328 ms,  elapsed time = 11479 ms.

Затем строка

Table 'table2'. Scan count 1, logical reads 2339848, physical reads 0, read-ahead reads 2303, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table3'. Scan count 1016, logical reads 4467, physical reads 21, read-ahead reads 1047, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 659, logical reads 5863, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table4'. Scan count 1, logical reads 126, physical reads 0, read-ahead reads 126, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table1'. Scan count 1033, logical reads 5228, physical reads 60, read-ahead reads 120, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table5'. Scan count 1, logical reads 219, physical reads 0, read-ahead reads 219, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'activeYear'. Scan count 1, logical reads 2, physical reads 2, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table6'. Scan count 1, logical reads 2, physical reads 2, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
   CPU time = 16719 ms,  elapsed time = 17447 ms.

(В этом объединении несколько таблиц больше, чем в упрощенном запросе, но меня интересует только изменение строки по сравнению с подзапросом в этом единственном соединении ... так что, надеюсь, это изолированно, поскольку t1 соединяется отдельно с все таблицы в этом запросе.)

1 Ответ

0 голосов
/ 27 марта 2011

обоснованное предположение: IN первого запроса преобразуется в JOIN:

SELECT * FROM table1 t1
JOIN table2 t2 ON t1.id = t2.id 
JOIN activeYears ay ON t2.time = ay.year 
WHERE ay.active = 1

в то время как IN 2-го запроса преобразуется в OR (или даже UNION):

SELECT * FROM table1 t1
JOIN table2 t2 ON t1.id = t2.id 
WHERE t2.time = '2009'
   OR t2.time = '2010'
...