it-swarm.com.de

Fehler beim Konvertieren von NVARCHAR in BIGINT in SUBQUERY mit WHERE-Klausel, nachdem ungültige Datensätze gefiltert wurden

Ich habe eine Tabelle mit einer NVARCHAR-Spalte, die Daten enthält, die nicht in BIGINT konvertiert werden können. Ich bin mir dessen sehr bewusst und habe es mit ISNUMERIC(BB.NVARCHARCOL) = 1 herausgefiltert. Trotzdem wird beim Versuch, die Daten mit der Angabe Error converting data type nvarchar to bigint.

Folgendes funktioniert einwandfrei (keine von SQL gemeldeten Fehler):

SELECT *
FROM myNormalTable AA INNER JOIN myBadTable BB ON BB.NVARCHARCOL = AA.MYBIGINTCOL
WHERE ISNUMERIC(BB.NVARCHARCOL) = 1

Folgendes löst den Fehler aus:

SELECT *
FROM (
    SELECT *
    FROM myNormalTable AA INNER JOIN myBadTable BB ON BB.NVARCHARCOL = AA.MYBIGINTCOL
    WHERE ISNUMERIC(BB.NVARCHARCOL) = 1
    ) ZZ 
WHERE ZZ.MYBIGINTCOL = 1234

Diese Variation wirft auch den Fehler:

SELECT *
FROM (
    SELECT *
    FROM myNormalTable AA INNER JOIN
        (SELECT CAST(NVARCHARCOL AS BIGINT) NVARCHARCOL FROM myBadTable WHERE ISNUMERIC(NVARCHARCOL) = 1) BB
      ON BB.NVARCHARCOL = AA.MYBIGINTCOL
    ) ZZ 
WHERE ZZ.MYBIGINTCOL = 1234

Beachten Sie, dass dies kein Fehler ist und alle Datensätze erfolgreich zurückgibt ...

SELECT CAST(NVARCHARCOL AS BIGINT) NVARCHARCOL FROM myBadTable WHERE ISNUMERIC(NVARCHARCOL) = 1

Ich konnte eine Lösung finden, die darin bestand, mein BIGINT in der Unterabfrage in NVARCHAR umzuwandeln:

SELECT *
FROM (
    SELECT *
    FROM myNormalTable AA INNER JOIN myBadTable BB ON BB.NVARCHARCOL = CAST(AA.MYBIGINTCOL AS NVARCHAR)
    WHERE ISNUMERIC(BB.NVARCHARCOL) = 1
    ) ZZ 
WHERE ZZ.MYBIGINTCOL = 1234

Wenn ich die Datensätze in eine temporäre Tabelle einfüge:

SELECT CAST(NVARCHARCOL AS BIGINT) NVARCHARCOL INTO #TEMP FROM myBadTable WHERE ISNUMERIC(NVARCHARCOL) = 1

Ich kann diese temporäre Tabelle erfolgreich in einer Unterabfrage verwenden:

SELECT *
FROM (
    SELECT *
    FROM myNormalTable AA INNER JOIN #TEMP BB ON BB.NVARCHARCOL = AA.MYBIGINTCOL
    WHERE ISNUMERIC(BB.NVARCHARCOL) = 1
    ) ZZ 
WHERE ZZ.MYBIGINTCOL = 1234

Was in aller Welt ist los? Es scheint, dass SQL sich weigert, die Unterabfrage zu verwenden, um eine kleinere Ergebnismenge zu erhalten, bevor die äußere Abfrage ausgeführt wird. Es möchte die gesamte Tabelle verwenden, unabhängig davon, was ich tue. SQL Server 2012 Developer Ausgabe

3
Brad

Verwenden Sie stattdessen try_cast().

select *
from (
  select *
  from myNormalTable AA
  inner join #TEMP BB on try_cast(BB.NVARCHARCOL as bigint) = AA.MYBIGINTCOL
  --where try_cast(BB.NVARCHARCOL as bigint) is not null /* not neccessary for inner join */
 ) ZZ
where ZZ.MYBIGINTCOL = 1234

In SQL Server 2012 und höher: Jede dieser Optionen gibt null zurück, wenn die Konvertierung anstelle eines Fehlers fehlschlägt.

3
SqlZim

SqlZim hat Ihnen bereits eine gute Methode gegeben, um den Fehler in seiner Antwort zu vermeiden. In der Frage und in den Kommentaren scheinen Sie jedoch neugierig zu sein, warum eine Abfrage einen Fehler auslöst und die andere nicht. Ich kann Ihr Problem reproduzieren:

CREATE TABLE dbo.X_BIGINT_TABLE (ID BIGINT NOT NULL);

INSERT INTO dbo.X_BIGINT_TABLE WITH (TABLOCK)
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values;

CREATE TABLE dbo.X_NVARCHAR_TABLE (ID_NV NVARCHAR(10) NOT NULL);

INSERT INTO dbo.X_NVARCHAR_TABLE WITH (TABLOCK)
SELECT TOP (999) CAST(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS NVARCHAR(10))
FROM master..spt_values

UNION ALL

SELECT 'ZOLTAN';

Diese Abfrage funktioniert gut:

SELECT *
FROM dbo.X_BIGINT_TABLE BI 
INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
WHERE ISNUMERIC(NV.ID_NV) = 1;

Diese Abfrage löst einen Fehler aus:

SELECT *
FROM (
    SELECT *
    FROM dbo.X_BIGINT_TABLE BI 
    INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
    WHERE ISNUMERIC(NV.ID_NV) = 1
) ZZ 
WHERE ZZ.ID = 500;

Nachricht 8114, Ebene 16, Status 5, Zeile 25

Fehler beim Konvertieren des Datentyps nvarchar in bigint.

Das SQL Server-Abfrageoptimierungsprogramm kann Elemente einer Abfrage nach eigenem Ermessen neu anordnen, um zu versuchen, einen Abfrageplan mit ausreichend geschätzten Kosten zu finden, sofern die Änderungen keine Auswirkungen auf die Endergebnisse der Abfrage haben. Um das Konzept zu veranschaulichen, gehen wir einen möglichen Weg durch, wie die zweite Abfrage überarbeitet werden kann. Dies ist nicht der eigentliche schrittweise Prozess, den das Abfrageoptimierungsprogramm für dieses Beispiel durchläuft. Beginnen Sie mit der Abfrage:

SELECT *
FROM (
    SELECT *
    FROM dbo.X_BIGINT_TABLE BI 
    INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
    WHERE ISNUMERIC(NV.ID_NV) = 1
) ZZ 
WHERE ZZ.ID = 500;

Drücken Sie das Prädikat nach unten:

SELECT *
FROM (
    SELECT *
    FROM dbo.X_BIGINT_TABLE BI 
    INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
    WHERE BI.ID = 500 AND ISNUMERIC(NV.ID_NV) = 1
) ZZ;

Die abgeleitete Tabelle wird nicht mehr benötigt. Entfernen Sie diese:

SELECT *
FROM dbo.X_BIGINT_TABLE BI 
INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
WHERE BI.ID = 500 AND ISNUMERIC(NV.ID_NV) = 1

Wir wissen, dass BI.ID = NV.ID_NV, So dass wir den Filter auf Z.ID Auch auf NV.ID_NV Anwenden können:

SELECT *
FROM dbo.X_BIGINT_TABLE BI 
INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
WHERE BI.ID = 500 AND ISNUMERIC(NV.ID_NV) = 1 AND NV.ID_NV = 500

Der Join muss nicht mehr als INNER JOIN Implementiert werden, da für beide Join-Spalten auf einen einzigen Wert gefiltert wird. Wir können als CROSS JOIN Umschreiben:

SELECT * 
FROM 
(
    SELECT *
    FROM dbo.X_BIGINT_TABLE BI 
    WHERE BI.ID = 500
) 
CROSS JOIN 
(
    SELECT *
    FROM dbo.X_NVARCHAR_TABLE NV
    WHERE ISNUMERIC(NV.ID_NV) = 1 AND NV.ID_NV = 500
);

Wenn wir uns den Abfrageplan für die zweite Abfrage ansehen, können wir feststellen, dass das Endergebnis der endgültigen transformierten Abfrage sehr ähnlich ist:

transformed query

Hier ist der Text des Filterprädikats als Referenz:

CONVERT_IMPLICIT(bigint,[SE_DB].[dbo].[X_NVARCHAR_TABLE].[ID_NV] as [NV].[ID_NV],0)=(500) 
AND isnumeric(CONVERT_IMPLICIT(varchar(20),[SE_DB].[dbo].[X_NVARCHAR_TABLE].[ID_NV] as [NV].[ID_NV],0))=(1)

Wenn SQL Server den Teil CONVERT_IMPLICIT Des Prädikats vor dem Teil isnumeric auswertet, wird eine Fehlermeldung angezeigt.

Vermeiden Sie es im Allgemeinen, sich beim Schreiben von SQL-Abfragen auf die implizite Reihenfolge der Operationen zu verlassen. Möglicherweise haben Sie eine Abfrage, die heute gut funktioniert, aber Fehler auslöst, wenn Daten zur Tabelle hinzugefügt werden oder wenn ein anderer Abfrageplan ausgewählt wird. Es gibt natürlich Ausnahmen. In der Praxis sehen Sie normalerweise die verschiedenen Teile einer CASE -Anweisung, die in der Reihenfolge ausgewertet werden sollen, in der Sie sie geschrieben haben. Selbst dann können Fehler auftreten, die Sie nicht erwartet haben . Sie können Teilen Ihrer Abfrage auch einen überflüssigen TOP hinzufügen, um eine bestimmte Reihenfolge von Vorgängen zu fördern. Betrachten Sie die folgende Abfrage:

SELECT *
FROM (
    SELECT TOP (9223372036854775807) *
    FROM dbo.X_BIGINT_TABLE BI 
    INNER JOIN dbo.X_NVARCHAR_TABLE NV ON BI.ID = NV.ID_NV
    WHERE ISNUMERIC(NV.ID_NV) = 1
    ) ZZ 
WHERE ZZ.ID = 500;

Sie und ich wissen, dass der TOP die Ergebnisse der Abfrage nicht ändert. Es gibt jedoch keine Garantie für den Optimierer, dass die abgeleitete Tabelle nicht mehr als 9223372036854775807 Zeilen zurückgibt, sodass der TOP ausgewertet werden muss. Technisch gesehen fragen wir in dieser Abfrage nach den ersten 9223372036854775807 Zeilen und möchten dann Zeilen mit einem ID herausfiltern, der sich von 500 unterscheidet. Wenn Sie das Prädikat ID = 500 In die abgeleitete Tabelle verschieben, können sich die Ergebnisse ändern, sodass SQL Server dies nicht ändert TU das. In diesem Beispiel wird die Abfrage fehlerfrei ausgeführt und die Filterung erfolgt ganz am Ende:

top plan

4
Joe Obbish