Warum verfügt diese Suche in BIGINT col über zusätzliche Operatoren für konstanten Scan, Skalarberechnung und verschachtelte Schleifen?


8

Wenn ich mir den tatsächlichen Exektionsplan einiger meiner Abfragen ansehe, stelle ich fest, dass Literalkonstanten, die in einer WHERE-Klausel verwendet werden, als verschachtelte Kette aus skalarem und konstantem Scan angezeigt werden .

SQL Studio Screenshot

Um dies zu reproduzieren, verwende ich die folgende Tabelle

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

Mit einigen Daten:

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

Wenn ich die folgende (Unsinn-) Abfrage ausführe:

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

Ich sehe, dass es eine Nested-Loop-Zeichnung im Ergebnis von Index Seek und einer Skalarberechnung (von einer Konstanten) macht.

Beachten Sie, dass das Literal größer als maxint ist. Es hilft zu schreiben CAST(2147483648 as BIGINT). Gibt es eine Idee, warum MSSQL dies auf den Ausführungsplan verzögert, und gibt es einen kürzeren Weg, dies zu vermeiden, als die Besetzung zu verwenden? Beeinflusst es auch gebundene Parameter für vorbereitete Anweisungen (von jtds JDBC)?

Die Skalarberechnung wird nicht immer durchgeführt (scheint index-suchspezifisch zu sein). Und manchmal zeigt der Abfrageanalysator es nicht grafisch an, sondern wie col1 < scalar(expr1000)in den Prädikateigenschaften.

Ich habe dies mit MS SSMS 2016 (13.0.16100.1) und SQL Server 2014 Expres Edition 64bit unter Windows 7 gesehen, aber ich denke, es ist ein allgemeines Verhalten.

Antworten:


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)

Zeigt Ihnen, dass das Literal 2147483648als interpretiert wird numeric(10,0). Dieses Verhalten geht auf die Einführung von bigintin SQL Server (2000) zurück.

Es gibt keine Syntax, die angibt, dass ein Literal wie folgt behandelt werden soll bigint- das Hinzufügen eines expliziten CASTist die beste Lösung. Der Artikel Dynamische Suche und versteckte implizite Konvertierungen beschreibt den Rest des Apparats im Plan.

Der Plan selbst zeigt, dass die verschachtelten Schleifen ein Suchprädikat haben

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

Sie können eine erweiterte Ereignissitzung verwenden, um festzustellen query_trace_column_values, ob diese wie folgt sind.

Geben Sie hier die Bildbeschreibung ein

Das XML im Plan zeigt dies ebenfalls

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

Dies bedeutet nicht , dass es buchstäblich macht einen Vergleich < nulleher

Die Bereichsgrenzausdrücke verwenden NULL, um an beiden Enden 'unbegrenzt' darzustellen. ( Quelle )

Der Nettoeffekt ist also, dass Ihr Abfrageprädikat von b.col1 > CAST(2147483648 AS NUMERIC(10, 0))immer noch mit einer Suche gegen endetb.col1 > CAST(2147483648 AS BIGINT)

Beeinflusst es auch gebundene Parameter für vorbereitete Anweisungen (von jtds JDBC)?

Ich habe jtds JDBC nicht verwendet, aber ich nehme an, Sie können damit Parameterdatentypen definieren. Wenn ja, stellen Sie einfach sicher, dass die Parameter den richtigen Datentyp haben, der mit der Spalte ( bigint) übereinstimmt, damit SQL Server nicht mit nicht übereinstimmenden Datentypen umgehen muss.


3

In Bezug auf meine Frage zu JDBC vorbereitete Aussagen. jTDS verwendet sp_prepare/ sp_execute(im Standardmodus prepareSQL=3).

Mit folgender Abfrage ( Quelle ):

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

Ich kann die vorbereitete Anweisung als von JTDS ausgegeben sehen und sie deklariert die Variable (@P0 bigint)...wie erwartet.

Das ist also alles gut und ich muss bedenken, dass es beim Ausprobieren der Ausführungspläne besser ist, lokal typisierte Variablen zu definieren, anstatt sie durch Literale zu ersetzen (und / oder sp_executeden zwischengespeicherten Ausführungsplan zu verwenden).


1
Alternativ können Sie es zur Regel machen, Ihre Literale immer auf den Typ der Spalte umzustellen, mit der Sie übereinstimmen.
Andriy M
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.