Seltsames Verhalten einer Subquery

10/03/2008 - 14:56 von Thomas Schremser | Report spam
Hallo allerseits!

Ich muß aus einer alten Datenbank Daten in eine Neue migrieren. Der Aufbau
der beiden Datenbanken ist àußerst unterschiedlich, so daß ich an einer
Stelle beim Import zu einem etwas grauslichen Hack greife:

Die alte Datenbank enthàlt eine Tabelle, die sinngemàß wie aufgebaut ist:

CREATE TABLE OldTable
(
OldTableID int IDENTITY(1, 1),
Data nvarchar(50)
)

Dazu ein paar Beispieldaten:

INSERT INTO OldTable(Data)
SELECT 'Homer'
UNION SELECT 'Marge'
UNION SELECT 'Bart'
UNION SELECT 'Lisa'
UNION SELECT 'Maggie'

Diese Daten sollen nun in die neue Tabelle importiert werden, was in etwa so
vor sich geht:

CREATE TABLE #CDS
(
[Key] char(1)
)
GO

INSERT INTO #CDS([Key])
SELECT 'S'
GO

CREATE TABLE NewTable
(
NewTableID int IDENTITY(1, 1),
KeyValue char(1),
Data nvarchar(50)
)
GO

INSERT INTO NewTable(KeyValue, Data)
SELECT TOP 3 C.[Key], CAST(O.OldTableID AS char(5)) + Data
FROM OldTable O
INNER JOIN #CDS C ON 1 = 1

Weiters enthàlt NewTable bereits Daten aus einer anderen Quelle mit einem
anderen Key:

INSERT INTO NewTable(KeyValue, Data)
SELECT 'N', 'Flanders'

Nun gibt es in der alten Tabelle Datensàtze, die bei meinem Import-Script
nicht in die neue Tabelle übernommen worden sind (im Beispiel durch TOP 3
simuliert). Diese möchte ich nun gerne ausgeben und hàtte dazu folgenden
Code verwendet:

SELECT *
FROM OldTable
WHERE OldTableID NOT IN
(
SELECT LEFT(Data, 5)
FROM NewTable
WHERE KeyValue = (
SELECT [Key]
FROM #CDS
)
)

Das führt aber zu der Fehlermeldung

Meldung 245, Ebene 16, Status 1, Zeile 7
Syntax error converting the nvarchar value 'Fland' to a column
of data type int.

Ändere ich aber diese Abfrage auf

DECLARE @Key char(1)

SELECT @Key = [Key]
FROM #CDS

SELECT *
FROM OldTable
WHERE OldTableID NOT IN
(
SELECT LEFT(Data, 5)
FROM NewTable
WHERE KeyValue = @Key
)

funktionierts einwandfrei. Hat jemand eine Erklàrung dafür, was an der
ersten Variante falsch ist?

TIA

Grüße
Thomas

Any problem in computer science can be solved with another layer
of indirection. But that usually will create another problem.
David Wheeler
 

Lesen sie die antworten

#1 Elmar Boye
10/03/2008 - 16:15 | Warnen spam
Thomas Schremser schrieb:
Ich muß aus einer alten Datenbank Daten in eine Neue migrieren. Der Aufbau
der beiden Datenbanken ist àußerst unterschiedlich, so daß ich an einer
Stelle beim Import zu einem etwas grauslichen Hack greife:

Die alte Datenbank enthàlt eine Tabelle, die sinngemàß wie aufgebaut ist:

INSERT INTO NewTable(KeyValue, Data)
SELECT TOP 3 C.[Key], CAST(O.OldTableID AS char(5)) + Data
FROM OldTable O
INNER JOIN #CDS C ON 1 = 1
Weiters enthàlt NewTable bereits Daten aus einer anderen Quelle mit einem
anderen Key:

INSERT INTO NewTable(KeyValue, Data)
SELECT 'N', 'Flanders'

Nun gibt es in der alten Tabelle Datensàtze, die bei meinem Import-Script
nicht in die neue Tabelle übernommen worden sind (im Beispiel durch TOP 3
simuliert). Diese möchte ich nun gerne ausgeben und hàtte dazu folgenden
Code verwendet:

SELECT *
FROM OldTable
WHERE OldTableID NOT IN
(
SELECT LEFT(Data, 5)
FROM NewTable
WHERE KeyValue = (
SELECT [Key]
FROM #CDS
)
)

Das führt aber zu der Fehlermeldung

Meldung 245, Ebene 16, Status 1, Zeile 7
Syntax error converting the nvarchar value 'Fland' to a column
of data type int.



Da OldTableId als Integer eine höhere Wertigkeit als ein (N)(VAR)CHAR
hat, wird das LEFT(Data, 5) implizit in einen Integer konvertiert,
was natürlich bei 'Fland' nicht klappen kann. Siehe:
<URL:http://msdn2.microsoft.com/de-de/li...9.aspx>

Funktionieren tàte
...
SELECT CASE WHEN ISNUMERIC(LEFT(Data, 5)) = 1
WHEN LEFT(Data, 5) ELSE NULL END
zumindest solange wie nicht die alten Daten zufàllig eine Zahl am
Anfang haben.

Besser wàre an solchen Stellen immer explizit konvertiert:
CAST(CASE WHEN ISNUMERIC(LEFT(Data, 5)) = 1
WHEN LEFT(Data, 5) ELSE NULL END AS integer)

dann weiss man auch spàter noch was da passieren soll(te).


Ändere ich aber diese Abfrage auf

DECLARE @Key char(1)

SELECT @Key = [Key]
FROM #CDS

SELECT *
FROM OldTable
WHERE OldTableID NOT IN
(
SELECT LEFT(Data, 5)
FROM NewTable
WHERE KeyValue = @Key
)

funktionierts einwandfrei.



Logischerweise, denn 'FLand' wird wegen KeyValue = 'N' bereits
vorher aussortiert, und somit kommen keine falschen Daten
dazwischen - vorerst zumindest ;-)

Die Frage am Ende wàre: Muss der Tabellenaufbau wirklich so sein?
Besser wàre eine Spalte für OldTableId einzufügen, und wenns
nur temporàr ist, die kann bei den Zusatz-Daten mit NULL
belegt werden.

Gruß Elmar

Ähnliche fragen