Wenn Sie die UNPIVOT
Funktion auf Daten anwenden, die nicht normalisiert sind, muss der Datentyp und die Länge in SQL Server identisch sein. Ich verstehe, warum der Datentyp gleich sein muss, aber warum erfordert UNPIVOT, dass die Länge gleich ist?
Angenommen, ich habe die folgenden Beispieldaten, die ich zum Deaktivieren benötige:
CREATE TABLE People
(
PersonId int,
Firstname varchar(50),
Lastname varchar(25)
)
INSERT INTO People VALUES (1, 'Jim', 'Smith');
INSERT INTO People VALUES (2, 'Jane', 'Jones');
INSERT INTO People VALUES (3, 'Bob', 'Unicorn');
Wenn ich versuche, die Spalten Firstname
und zu UNPIVOTEN Lastname
, ähnlich wie in:
select PersonId, ColumnName, Value
from People
unpivot
(
Value
FOR ColumnName in (FirstName, LastName)
) unpiv;
SQL Server generiert den Fehler:
Nachricht 8167, Ebene 16, Status 1, Zeile 6
Der Spaltentyp "Nachname" steht in Konflikt mit dem Typ anderer Spalten, die in der UNPIVOT-Liste angegeben sind.
Um den Fehler zu beheben, müssen wir zuerst eine Unterabfrage verwenden, um die Lastname
Spalte mit der folgenden Länge zu konvertieren Firstname
:
select PersonId, ColumnName, Value
from
(
select personid,
firstname,
cast(lastname as varchar(50)) lastname
from People
) d
unpivot
(
Value FOR
ColumnName in (FirstName, LastName)
) unpiv;
Siehe SQL Fiddle with Demo
Bevor UNPIVOT in SQL Server 2005 eingeführt wurde, würde ich ein SELECT
mit verwenden, UNION ALL
um das Pivot der firstname
/ lastname
-Spalten aufzuheben , und die Abfrage würde ausgeführt, ohne dass die Spalten in dieselbe Länge konvertiert werden müssen:
select personid, 'firstname' ColumnName, firstname value
from People
union all
select personid, 'LastName', LastName
from People;
Siehe SQL Fiddle with Demo .
Wir sind auch in der Lage, die Pivot-Funktion der Daten erfolgreich zu deaktivieren, CROSS APPLY
ohne die gleiche Länge für den Datentyp zu haben:
select PersonId, columnname, value
from People
cross apply
(
select 'firstname', firstname union all
select 'lastname', lastname
) c (columnname, value);
Siehe SQL Fiddle with Demo .
Ich habe MSDN durchgelesen, aber ich habe keine Erklärung gefunden, warum die Länge des Datentyps gleich ist.
Welche Logik steckt dahinter, wenn bei UNPIVOT dieselbe Länge erforderlich ist?
quelle
Antworten:
Diese Frage kann nur von den Personen wirklich beantwortet werden, die an der Umsetzung von gearbeitet haben
UNPIVOT
. Sie können dies möglicherweise erhalten, indem Sie sich an sie wenden, um Unterstützung zu erhalten . Das Folgende ist mein Verständnis der Argumentation, die möglicherweise nicht 100% genau ist:T-SQL enthält eine beliebige Anzahl von Instanzen seltsamer Semantik und anderer kontraintuitiver Verhaltensweisen. Einige von ihnen werden irgendwann als Teil der Abschreibungszyklen verschwinden, andere werden jedoch möglicherweise nie "verbessert" oder "behoben". Abgesehen von allem anderen gibt es Anwendungen, die von diesem Verhalten abhängen. Daher muss die Abwärtskompatibilität erhalten bleiben.
Die Regeln für implizite Konvertierungen und die Ableitung von Ausdruckstypen machen einen erheblichen Teil der oben genannten Verrücktheit aus. Ich beneide die Tester nicht, die sicherstellen müssen, dass das seltsame (und oft nicht dokumentierte) Verhalten (unter allen Kombinationen von
SET
Sitzungswerten usw.) für neue Versionen beibehalten wird .Trotzdem gibt es keinen guten Grund, bei der Einführung neuer Sprachfunktionen (mit offensichtlich keinem Gepäck für die Abwärtskompatibilität) keine Verbesserungen vorzunehmen und frühere Fehler zu vermeiden. Neue Funktionen wie rekursive allgemeine Tabellenausdrücke (wie von Andriy M in einem Kommentar erwähnt) und
UNPIVOT
relativ gesunde Semantik und klar definierte Regeln.Es wird eine Reihe von Ansichten darüber geben, ob die Einbeziehung der Länge in den Typ eine zu weit gehende explizite Eingabe erfordert, aber ich persönlich begrüße dies. Aus meiner Sicht sind die Typen
varchar(25)
undvarchar(50)
sind nicht die gleichen, sowenigdecimal(8)
unddecimal(10)
sind. Eine spezielle Konvertierung von Zeichenfolgen in Groß- und Kleinschreibung erschwert die Arbeit unnötig und bietet meiner Meinung nach keinen wirklichen Mehrwert.Man könnte argumentieren, dass nur implizite Konvertierungen, die Daten verlieren könnten, explizit angegeben werden müssen, aber es gibt auch Randfälle. Letztendlich wird eine Konvertierung erforderlich sein, die wir genauso gut explizit machen können.
Wenn die implizite Konvertierung von
varchar(25)
nachvarchar(50)
erlaubt wäre, wäre es nur eine andere (höchstwahrscheinlich verborgene) implizite Konvertierung mit all den üblichen seltsamen Randfällen und demSET
Setzen von Empfindlichkeiten. Warum nicht die Implementierung so einfach und explizit wie möglich gestalten? (Nichts ist perfekt, aber, und es ist eine Schande , dass verstecktvarchar(25)
und imvarchar(50)
Innern einessql_variant
erlaubt ist.)Das Umschreiben von
UNPIVOT
withAPPLY
undUNION ALL
vermeidet das (bessere) Typverhalten, da die Regeln fürUNION
die Abwärtskompatibilität unterliegen und in der Onlinedokumentation so dokumentiert sind, dass sie verschiedene Typen zulassen, sofern sie mit impliziter Konvertierung vergleichbar sind (für die die arkanen Regeln des Datentyps Vorrang haben) verwendet werden, und so weiter).Die Problemumgehung besteht darin, die Datentypen explizit anzugeben und bei Bedarf explizite Konvertierungen hinzuzufügen. Das sieht für mich nach Fortschritt aus :)
Eine Möglichkeit, die explizit eingegebene Problemumgehung zu schreiben:
Beispiel für einen rekursiven CTE:
Beachten Sie schließlich, dass das Umschreiben
CROSS APPLY
in der Frage nicht ganz mit dem in der Frage übereinstimmtUNPIVOT
, daNULL
Attribute nicht abgelehnt werden.quelle
Der
UNPIVOT
Bediener nutzt denIN
Bediener. Die Angaben für den IN-Operator (Abbildung unten) geben an, dass sowohl dertest_expression
(in diesem Fall der linkeIN
) als auch derexpression
(der rechteIN
) Datentyp identisch sein müssen. Aufgrund der transitiven Eigenschaft der Gleichheit muss jeder Ausdruck ebenfalls vom gleichen Datentyp sein.quelle