Por que essa busca no BIGINT col possui operadores de varredura extra constante, escalares computacionais e loops aninhados?

8

Quando olho para o plano de exposição real de algumas de minhas consultas, percebo que as constantes literais usadas em uma cláusula WHERE aparecem como uma cadeia aninhada de cálculo de varredura escalar e constante .

captura de tela do sql studio

Para reproduzir isso, eu uso a tabela a seguir

CREATE TABLE Table1 (
    [col1] [bigint] NOT NULL,
    [col2] [varchar](50) NULL,
    [col3] [char](200) NULL
)
CREATE NONCLUSTERED INDEX IX_Table1 ON Table1 (col1 ASC)

Com alguns dados:

INSERT INTO Table1(col1) VALUES (1),(2),(3),
                               (-9223372036854775808),
                               (9223372036854775807),
                               (2147483647),(-2147483648)

Quando executo a seguinte consulta (sem sentido):

SELECT a.col1, a.col2
  FROM Table1 a, Table1 b
  WHERE b.col1 > 2147483648

Vejo que ele fará um desenho de loop aninhado no resultado da busca por índice e um cálculo escalar (a partir de uma constante).

Observe que o literal é maior que maxint. Ajuda escrever CAST(2147483648 as BIGINT). Alguma idéia de por que o MSSQL está adiando isso para o plano de execução e existe uma maneira mais curta de evitá-lo do que usar o elenco? Isso afeta parâmetros vinculados a instruções preparadas (do jtds JDBC) também?

O cálculo escalar nem sempre é feito (parece ser o índice de busca específico). E, às vezes, o analisador de consultas não o mostra graficamente, mas como col1 < scalar(expr1000)nas propriedades do predicado.

Eu já vi isso com o MS SSMS 2016 (13.0.16100.1) e o SQL Server 2014 Expres Edition 64bit no Windows 7, mas acho que é um comportamento geral.

eckes
fonte

Respostas:

8
SELECT thing, 
       sql_variant_property(thing,'basetype') AS basetype,
       sql_variant_property(thing,'precision') AS precision, 
       sql_variant_property(thing,'scale') AS scale
FROM (VALUES (2147483648)) V(thing)

Mostra que o literal 2147483648é interpretado como numeric(10,0). Esse comportamento antecede a introdução do bigintno SQL Server (2000).

Não há sintaxe para indicar que um literal deve ser tratado como bigint- adicionar um explícito CASTé a melhor solução. O artigo Busca dinâmica e conversões implícitas ocultas discutem o restante do aparelho no plano.

O plano em si mostra que os loops aninhados têm um predicado de busca em

Seek Keys[1]: Start: [tempdb].[dbo].[Table1].col1 > Scalar Operator([Expr1005]), 
                End: [tempdb].[dbo].[Table1].col1 < Scalar Operator([Expr1006])

Você pode usar uma sessão de eventos estendida query_trace_column_valuespara verificar se são os seguintes.

insira a descrição da imagem aqui

O XML no plano também mostra isso

  <DefinedValue>
    <ValueVector>
      <ColumnReference Column="Expr1005" />
      <ColumnReference Column="Expr1006" />
      <ColumnReference Column="Expr1004" />
    </ValueVector>
    <ScalarOperator ScalarString="GetRangeWithMismatchedTypes((2147483648.),NULL,(6))">
      <Intrinsic FunctionName="GetRangeWithMismatchedTypes">
        <ScalarOperator>
          <Const ConstValue="(2147483648.)" />
        </ScalarOperator>
        <ScalarOperator>
          <Const ConstValue="NULL" />
        </ScalarOperator>
        <ScalarOperator>
          <Const ConstValue="(6)" />
        </ScalarOperator>
      </Intrinsic>
    </ScalarOperator>
  </DefinedValue>

Isso não significa que está literalmente fazendo uma comparação < null em vez

As expressões de limite do intervalo usam NULL para representar 'ilimitado' em cada extremidade. ( Fonte )

Portanto, o efeito líquido é que seu predicado de consulta de b.col1 > CAST(2147483648 AS NUMERIC(10, 0)) ainda termina com uma busca contrab.col1 > CAST(2147483648 AS BIGINT)

Isso afeta parâmetros vinculados a instruções preparadas (do jtds JDBC) também?

Eu não usei o jtds JDBC, mas presumo que ele permite definir tipos de dados de parâmetro? Nesse caso, verifique se os parâmetros são do tipo de dados correto que correspondem à coluna ( bigint), para que o SQL Server não precise lidar com tipos de dados incompatíveis.

Martin Smith
fonte
3

Em relação à minha pergunta sobre as declarações preparadas pelo JDBC. O jTDS usa sp_prepare/ sp_execute(no prepareSQL=3modo padrão ).

Com a seguinte consulta ( origem ):

use database
select
    cp.objtype, st.text
from sys.dm_exec_cached_plans cp
cross apply sys.dm_exec_sql_text(cp.plan_handle) st
where cp.objtype = 'prepared' and st.text like '%TABLE%'

Eu posso ver a declaração preparada emitida pelo JTDS e ela declara a variável conforme (@P0 bigint)...o esperado.

Portanto, tudo isso é bom e preciso lembrar que, ao testar os planos de execução, é melhor definir variáveis ​​de tipo local em vez de substituí-las por literais (e / ou usar sp_executepara explorar o plano de execução em cache).

eckes
fonte
11
Como alternativa, você pode estabelecer uma regra para sempre converter seus literais no tipo de coluna em que você está correspondendo.
Andriy H