Pourquoi cette recherche sur BIGINT col a-t-elle des opérateurs de balayage constant supplémentaire, de calcul scalaire et de boucles imbriquées?

8

Lorsque je regarde le plan d'exection réel de certaines de mes requêtes, je remarque que les constantes littérales utilisées dans une clause WHERE apparaissent comme une chaîne imbriquée de calcul scalaire et de balayage constant .

capture d'écran sql studio

Pour reproduire cela, j'utilise le tableau suivant

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

Avec quelques données:

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

Lorsque j'exécute la requête (non-sens) suivante:

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

Je vois qu'il fera un dessin de boucle imbriquée dans le résultat de la recherche d'index et un calcul scalaire (à partir d'une constante).

Notez que le littéral est plus grand que maxint. Cela aide à écrire CAST(2147483648 as BIGINT). Avez-vous une idée de la raison pour laquelle MSSQL transfère cela au plan d'exécution et existe-t-il un moyen plus court de l'éviter que d'utiliser la distribution? Cela affecte-t-il également les paramètres liés aux instructions préparées (à partir de jtds JDBC)?

Le calcul scalaire n'est pas toujours fait (semble être spécifique à la recherche d'index ). Et parfois, l'analyseur de requêtes ne l'affiche pas graphiquement mais comme col1 < scalar(expr1000)dans les propriétés de prédicat.

J'ai vu cela avec MS SSMS 2016 (13.0.16100.1) et SQL Server 2014 Expres Edition 64bit sur Windows 7, mais je suppose que c'est un comportement général.

eckes
la source

Réponses:

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)

Vous montre que le littéral 2147483648est interprété comme numeric(10,0). Ce comportement est antérieur à l'introduction de la bigintdans SQL Server (2000).

Il n'y a pas de syntaxe pour indiquer qu'un littéral doit être traité comme bigint- l'ajout d'un explicite CASTest la meilleure solution. L'article Recherches dynamiques et conversions implicites cachées traite du reste de l'appareil dans le plan.

Le plan lui-même montre que les boucles imbriquées ont un prédicat de recherche sur

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

Vous pouvez utiliser une session d'événements étendue sur query_trace_column_valuespour voir que celles-ci sont les suivantes.

entrez la description de l'image ici

Le XML dans le plan montre également cela

  <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>

Cela ne signifie pas qu'il fait littéralement une comparaison < nullplutôt

Les expressions de limites de plage utilisent NULL pour représenter «sans limite» à chaque extrémité. ( Source )

Donc, l'effet net est que votre prédicat de requête b.col1 > CAST(2147483648 AS NUMERIC(10, 0))se termine toujours par une recherche contreb.col1 > CAST(2147483648 AS BIGINT)

Cela affecte-t-il également les paramètres liés aux instructions préparées (à partir de jtds JDBC)?

Je n'ai pas utilisé jtds JDBC mais je suppose qu'il vous permet de définir des types de données de paramètres? Si tel est le cas, assurez-vous simplement que les paramètres sont le type de données correct qui correspond à la colonne ( bigint) afin qu'il n'y ait pas besoin pour SQL Server de traiter les types de données incompatibles.

Martin Smith
la source
3

Concernant ma question sur les déclarations préparées par JDBC. jTDS utilise sp_prepare/ sp_execute(en prepareSQL=3mode par défaut ).

Avec la requête suivante ( source ):

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%'

Je peux voir l'instruction préparée telle qu'émise par JTDS, et elle déclare la variable (@P0 bigint)...comme prévu.

Donc, tout cela est bon et je dois me rappeler que lors de l'essai des plans d'exécution, il est préférable de définir réellement les variables typées locales au lieu de les remplacer par des littéraux (et / ou d'utiliser sp_executepour puiser dans le plan d'exécution mis en cache).

eckes
la source
1
Alternativement, vous pouvez en faire une règle avec vous pour toujours convertir vos littéraux en fonction du type de colonne que vous comparez.
Andriy M