Mengapa pencarian ini pada BIGINT col memiliki pemindaian ekstra konstan, menghitung skalar, dan operator loop bersarang?

8

Ketika saya melihat rencana ekseksi aktual dari beberapa pertanyaan saya, saya perhatikan bahwa konstanta literal yang digunakan dalam klausa WHERE muncul sebagai rantai bersarang untuk penghitungan skalar dan pemindaian konstan .

tangkapan layar sql studio

Untuk mereproduksi ini, saya menggunakan tabel berikut

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

Dengan beberapa data di dalamnya:

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

Ketika saya menjalankan kueri (omong kosong) berikut:

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

Saya melihat bahwa itu akan melakukan menggambar Nested Loop di hasil Index Seek dan perhitungan skalar (dari konstanta).

Perhatikan bahwa literal lebih besar dari maxint. Memang membantu menulis CAST(2147483648 as BIGINT). Tahu mengapa MSSQL membelokkan itu ke rencana eksekusi dan apakah ada cara yang lebih pendek untuk menghindarinya daripada menggunakan para pemain? Apakah itu mempengaruhi parameter terikat untuk pernyataan yang disiapkan (dari jtds JDBC) juga?

Perhitungan skalar tidak selalu dilakukan (tampaknya indeks pencarian spesifik). Dan kadang-kadang penganalisis permintaan tidak menunjukkannya secara grafis tetapi seperti col1 < scalar(expr1000)dalam properti predikat.

Saya telah melihat ini dengan MS SSMS 2016 (13.0.16100.1) dan SQL Server 2014 Expres Edition 64bit pada Windows 7, tapi saya rasa ini adalah perilaku umum.

eek
sumber

Jawaban:

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)

Menunjukkan kepada Anda bahwa literal 2147483648ditafsirkan sebagai numeric(10,0). Perilaku ini pra-tanggal pengenalan bigintdi SQL Server (2000).

Tidak ada sintaks untuk menunjukkan bahwa literal harus diperlakukan sebagai bigint- menambahkan eksplisit CASTadalah solusi terbaik. Artikel Dynamic Seeks and Hidden Implicit Conversions membahas sisa peralatan dalam rencana.

Rencana itu sendiri menunjukkan bahwa loop bersarang memiliki predikat pencarian

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

Anda dapat menggunakan sesi acara diperpanjang query_trace_column_valuesuntuk melihat bahwa ini adalah sebagai berikut.

masukkan deskripsi gambar di sini

XML dalam paket juga menunjukkan ini

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

Ini tidak berarti bahwa secara harfiah melakukan perbandingan < nullyang agak

Ekspresi batas rentang menggunakan NULL untuk mewakili 'tidak terikat' di kedua ujungnya. ( Sumber )

Jadi, efek bersihnya adalah bahwa predikat kueri Anda b.col1 > CAST(2147483648 AS NUMERIC(10, 0))masih berakhir dengan pencarian menentangb.col1 > CAST(2147483648 AS BIGINT)

Apakah itu mempengaruhi parameter terikat untuk pernyataan yang disiapkan (dari jtds JDBC) juga?

Saya belum pernah menggunakan jtds JDBC tapi saya kira itu memungkinkan Anda untuk menentukan tipe data parameter? Jika demikian, pastikan parameternya adalah tipe data yang benar yang cocok dengan kolom ( bigint) sehingga SQL Server tidak perlu berurusan dengan tipe data yang tidak cocok.

Martin Smith
sumber
3

Sehubungan dengan pertanyaan saya tentang pernyataan disiapkan JDBC. jTDS menggunakan sp_prepare/ sp_execute(dalam prepareSQL=3mode default ).

Dengan kueri ( sumber ) berikut:

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

Saya bisa melihat pernyataan yang disiapkan seperti yang dikeluarkan oleh JTDS, dan itu menyatakan variabel (@P0 bigint)...seperti yang diharapkan.

Jadi ini semua baik dan saya harus ingat bahwa ketika mencoba rencana eksekusi itu lebih baik untuk benar-benar mendefinisikan variabel yang diketik lokal daripada menggantinya dengan literal (dan / atau menggunakan sp_executeuntuk memanfaatkan rencana eksekusi yang di-cache).

eek
sumber
1
Atau Anda bisa membuatnya menjadi aturan dengan Anda selalu melemparkan literal ke jenis kolom yang cocok dengan Anda.
Andriy M