Perché questa ricerca su BIGINT col ha operatori di scansione extra costante, calcolo scalare e cicli annidati?


8

Quando osservo il piano di exection reale di alcune delle mie query, noto che le costanti letterali utilizzate in una clausola WHERE si presentano come una catena nidificata di calcoli di scansione scalare e costante .

screenshot di sql studio

Per riprodurre questo, uso la seguente tabella

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

Con alcuni dati al suo interno:

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

Quando eseguo la seguente query (senza senso):

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

Vedo che eseguirà un disegno ad anello annidato nel risultato di Index Seek e un calcolo scalare (da una costante).

Si noti che il valore letterale è maggiore di maxint. Aiuta a scrivere CAST(2147483648 as BIGINT). Qualche idea sul perché MSSQL lo sta deframmentando al piano di esecuzione ed esiste un modo più breve per evitarlo rispetto all'utilizzo del cast? Influisce anche sui parametri associati alle istruzioni preparate (da jtds JDBC)?

Il calcolo scalare non viene sempre eseguito (sembra essere un indice specifico per la ricerca ). E a volte l'analizzatore di query non lo mostra graficamente ma come col1 < scalar(expr1000)nelle proprietà del predicato.

Ho visto questo con MS SSMS 2016 (13.0.16100.1) e SQL Server 2014 Expres Edition 64 bit su Windows 7, ma suppongo sia un comportamento generale.

Risposte:


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)

Ti mostra che il letterale 2147483648viene interpretato come numeric(10,0). Questo comportamento precede l'introduzione di bigintin SQL Server (2000).

Non c'è sintassi per indicare che un letterale dovrebbe essere trattato come bigint- l'aggiunta di un esplicito CASTè la soluzione migliore. L'articolo Ricerca dinamica e conversioni implicite nascoste discute il resto dell'apparato nel piano.

Il piano stesso mostra che i loop nidificati hanno un predicato di ricerca

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

È possibile utilizzare una sessione di eventi estesa query_trace_column_valuesper vedere che questi sono i seguenti.

inserisci qui la descrizione dell'immagine

Anche l'XML nel piano mostra questo

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

Questo non vuol dire che sta letteralmente facendo un confronto < nullpiuttosto

Le espressioni del limite dell'intervallo utilizzano NULL per rappresentare "illimitato" alle due estremità. ( Fonte )

Quindi l'effetto netto è che il predicato della tua query b.col1 > CAST(2147483648 AS NUMERIC(10, 0))finisce ancora con una ricerca controb.col1 > CAST(2147483648 AS BIGINT)

Influisce anche sui parametri associati alle istruzioni preparate (da jtds JDBC)?

Non ho usato jtds JDBC ma presumo che ti consenta di definire i tipi di dati dei parametri? In tal caso, assicurati solo che i parametri siano il tipo di dati corretto che corrisponda alla colonna ( bigint), quindi non è necessario che SQL Server gestisca tipi di dati non corrispondenti.


3

In relazione alla mia domanda sulle dichiarazioni preparate da JDBC. jTDS utilizza sp_prepare/ sp_execute(in prepareSQL=3modalità predefinita ).

Con la seguente query ( sorgente ):

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

Vedo la dichiarazione preparata emessa da JTDS e dichiara la variabile (@P0 bigint)...come previsto.

Quindi va tutto bene e devo ricordare che quando si provano i piani di esecuzione è meglio definire effettivamente le variabili tipizzate locali invece di sostituirle con valori letterali (e / o usare sp_executeper attingere al piano di esecuzione memorizzato nella cache).


1
In alternativa puoi impostare una regola con te per trasmettere sempre i tuoi letterali al tipo di colonna a cui stai confrontando.
Andriy M,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.