it-swarm.com.de

CAST-Auslösefehler beim Ausführen in einer gespeicherten Prozedur, jedoch nicht beim Ausführen als Rohabfrage

Hier passiert etwas sehr Seltsames.

Ich habe eine Abfrage, die so aussieht.

SELECT CAST(FT.DOP AS SMALLINT) FROM TRACKING_DATA WHERE date > @mydate and identifier = 0000000000

Wenn es als Rohabfrage ausgeführt wird, werden Daten einwandfrei zurückgegeben.

Wenn ich es in eine gespeicherte Prozedur lege, die die where-Klausel ändert, löst es diesen Fehler aus.

Msg 244, Level 16, State 2, Procedure myprocedure, Line 107 [Batch Start Line 2]
The conversion of the varchar value '58629' overflowed an INT2 column. Use a larger integer column.

Also hier ist die Kuriosität. Ich gehe alle möglichen Daten für diese where-Klausel mit einer solchen Abfrage durch.

SELECT DISTINCT DOP FROM TRACKING_DATA where identifier = 000000000000

und

SELECT DISTINCT CAST(DOP AS smallint) FROM TRACKING_DATA where identifier = 000000000000

Und das bekomme ich zurück.

17
12
9
19
8
14
6
16
11
13
7
10
0
18
5
15
4

Nirgendwo ist etwas für ein SMALLINT entfernt zu groß. Also dachte ich, ok, vielleicht ist es ein nicht druckbares ASCII Zeichen. Aber ich kann keines finden.

Ich bin im Moment ein wenig ratlos. Es führt find als Rohabfrage aus, explodiert als Prozedur und alle möglichen Daten basierend auf dem Gültigkeitsort. Mein einziger Verdacht ist, dass der Abfrageplan beim Filtern etwas Seltsames tut oder möglicherweise mit einer anderen Validierung ausgeführt wird, wenn er als Prozedur ausgeführt wird.

8
Chris Rice

Es ist besser, wenn Sie nicht von der Indizierung abhängig sind, um diese Fehler zu vermeiden, sondern stattdessen die Abfrage schreiben, um sich vor dieser Situation zu schützen. Da Sie mit SQL Server 2012 arbeiten, besteht eine Option darin, TRY_CAST zu verwenden:

SELECT TRY_CAST(FT.DOP AS SMALLINT) 
FROM TRACKING_DATA 
WHERE date > @mydate and identifier = 0000000000;

Dies führt dazu, dass NULL für Werte ausgewählt wird, die nicht von varchar in smallint konvertiert werden können. Aber solange Sie wissen, dass es solche Ergebnisse nicht gibt oder Ihre Anwendung die NULL -Ergebnisse verarbeiten kann, sollten Sie gut sein.

13
Josh Darnell

Es war also der verdammte Abfrageplan, für den er sich immer wieder entschied.

Der Wert, auf dem es explodierte, war in der Tabelle vorhanden (was es nicht sein sollte, aber das ist ein weiteres Problem), aber es wurde mit einem völlig anderen Bezeichner verknüpft.

Die fragliche Abfrage führte ein clustered seek für einen Index, der das date, aber nicht das identifier abdeckte, führte dies dazu, dass die GESAMTE Tabelle gescannt wurde, was aus einer Reihe von Gründen so, so, so falsch ist.

Es wurde ein geeigneter Index für die where -Klausel hinzugefügt, und die Prozedur summt jetzt glücklich weg, da sie nach dem Bezeichner filtert, bevor sie nach dem Datum geht. Ich wünschte, ich könnte vorher/nachher Statistiken bekommen, aber ich bin mir ziemlich sicher, dass es jetzt auch schneller läuft.

12
Chris Rice

Sie erhalten offensichtlich unterschiedliche Abfragepläne und Ihre Besetzung wird gegen Werte getestet, die durch die where-Klausel herausgefiltert werden. Ich würde nicht versuchen herauszufinden, warum - aus welchen Gründen auch immer, es könnte offensichtlich aus einem anderen Grund später passieren. Sich auf das Filtern zu verlassen ist unzuverlässig.

Sie müssen eine Abfrage schreiben, die unabhängig von Index, Hinweis oder Statistik nicht fehlschlägt.

Ich denke, es gibt drei Möglichkeiten, dies zu tun:

  1. Verwenden Sie eine temporäre Tabelle/Tabellenvariable.

    declare @result table (dop varchar);
    Insert into @result
    SELECT FT.DOP  FROM TRACKING_DATA 
    WHERE date > @mydate and identifier = 0000000000
    
    SELECT cast(dop as SMALLINT) dop from @result
    
  2. Verwenden Sie eine case-Anweisung:

    SELECT CAST(case when FT.DOP like '%[^0-9]%' then null else ft.dop end AS SMALLINT) 
    FROM TRACKING_DATA 
    WHERE date > @mydate and identifier = 0000000000
    
  3. Verwenden Sie try_cast anstelle von cast:

    SELECT try_CAST(FT.DOP AS SMALLINT) 
    FROM TRACKING_DATA 
    WHERE date > @mydate and identifier = 0000000000
    

Persönlich würde ich 3 empfehlen, wenn möglich.

1
jmoreno