Lors de l'application de la UNPIVOT
fonction à des données qui ne sont pas normalisées, SQL Server requiert que le type de données et la longueur soient identiques. Je comprends pourquoi le type de données doit être le même, mais pourquoi UNPIVOT exige-t-il que la longueur soit la même?
Disons que j'ai les exemples de données suivants que je dois débloquer:
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');
Si j'essaye de UNPIVOT les colonnes Firstname
et Lastname
similaires à:
select PersonId, ColumnName, Value
from People
unpivot
(
Value
FOR ColumnName in (FirstName, LastName)
) unpiv;
SQL Server génère l'erreur:
Msg 8167, niveau 16, état 1, ligne 6
Le type de colonne "Nom" est en conflit avec le type des autres colonnes spécifiées dans la liste UNPIVOT.
Afin de résoudre l'erreur, nous devons utiliser une sous-requête pour lancer d'abord la Lastname
colonne pour avoir la même longueur que 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;
Voir SQL Fiddle avec démo
Avant d'introduire UNPIVOT dans SQL Server 2005, j'utilisais un SELECT
avec UNION ALL
pour annuler le pivotement des colonnes firstname
/ lastname
et la requête s'exécuterait sans avoir besoin de convertir les colonnes à la même longueur:
select personid, 'firstname' ColumnName, firstname value
from People
union all
select personid, 'LastName', LastName
from People;
Voir SQL Fiddle avec démo .
Nous sommes également en mesure de débloquer les données avec succès CROSS APPLY
sans avoir la même longueur sur le type de données:
select PersonId, columnname, value
from People
cross apply
(
select 'firstname', firstname union all
select 'lastname', lastname
) c (columnname, value);
Voir SQL Fiddle avec démo .
J'ai lu MSDN mais je n'ai rien trouvé qui explique le raisonnement pour forcer la longueur du type de données à être la même.
Quelle est la logique derrière l'exigence de la même longueur lors de l'utilisation d'UNPIVOT?
la source
Réponses:
Cette question ne peut être véritablement répondue que par les personnes qui ont travaillé à la mise en œuvre de
UNPIVOT
. Vous pourrez peut-être l'obtenir en les contactant pour obtenir de l'aide . Ce qui suit est ma compréhension du raisonnement, qui peut ne pas être exact à 100%:T-SQL contient un nombre illimité d'instances de sémantique étrange et d'autres comportements contre-intuitifs. Certains d'entre eux finiront par disparaître dans le cadre des cycles de dépréciation, mais d'autres pourraient ne jamais être «améliorés» ou «fixés». En dehors de toute autre chose, il existe des applications qui dépendent de ces comportements, donc la compatibilité descendante doit être préservée.
Les règles relatives aux conversions implicites et à la dérivation du type d'expression représentent une proportion importante de l'étrangeté mentionnée ci-dessus. Je n'envie pas les testeurs qui doivent s'assurer que les comportements étranges (et souvent non documentés) sont préservés (sous toutes les combinaisons de
SET
valeurs de session et ainsi de suite) pour les nouvelles versions.Cela dit, il n'y a aucune bonne raison de ne pas apporter d'améliorations et d'éviter les erreurs passées lors de l'introduction de nouvelles fonctionnalités de langage (sans évidemment aucun bagage de compatibilité descendante). De nouvelles fonctionnalités comme les expressions de table communes récursives (comme mentionné par Andriy M dans un commentaire) et
UNPIVOT
étaient libres d'avoir une sémantique relativement saine et des règles clairement définies.Il y aura un large éventail de vues pour savoir si l'inclusion de la longueur dans le type va trop loin dans la saisie explicite, mais personnellement je m'en réjouis. À mon avis, les types
varchar(25)
et nevarchar(50)
sont pas les mêmes, pas plus quedecimal(8)
et ne ledecimal(10)
sont. La conversion spéciale de type chaîne de boîtier complique les choses inutilement et n'ajoute aucune valeur réelle, à mon avis.On pourrait faire valoir que seules les conversions implicites susceptibles de perdre des données devraient être explicitement énoncées, mais il existe également des cas limites. En fin de compte, une conversion va être nécessaire, alors nous pourrions aussi bien la rendre explicite.
Si la conversion implicite de
varchar(25)
àvarchar(50)
était autorisée, ce ne serait qu'une autre conversion implicite (très probablement cachée), avec tous les cas de bord étranges habituels et lesSET
sensibilités définies. Pourquoi ne pas rendre l'implémentation la plus simple et la plus explicite possible? (Cependant, rien n'est parfait et c'est dommage que se cachervarchar(25)
et à l'varchar(50)
intérieur d'unsql_variant
soit autorisé.)La réécriture de
UNPIVOT
withAPPLY
etUNION ALL
évite le comportement de type (meilleur) car les règles deUNION
sont sujettes à une compatibilité descendante et sont documentées dans la documentation en ligne comme autorisant différents types tant qu'elles sont comparables à l'aide de la conversion implicite (pour lesquelles les règles obscures de priorité des types de données sont utilisés, etc.).La solution de contournement consiste à être explicite sur les types de données et à ajouter des conversions explicites si nécessaire. Cela ressemble à un progrès pour moi :)
Une façon d'écrire la solution de contournement explicitement typée:
Exemple CTE récursif:
Enfin, notez que la réécriture à l'aide
CROSS APPLY
de la question n'est pas tout à fait la même que laUNPIVOT
, car elle ne rejette pas lesNULL
attributs.la source
L'
UNPIVOT
opérateur utilise l'IN
opérateur. Les spécifications de l'opérateur IN (capture d'écran ci-dessous) indiquent que letest_expression
(dans ce cas, à gauche deIN
) et chacunexpression
(à droite deIN
) doivent être du même type de données. Grâce à la propriété transitive d'égalité, chaque expression doit également être du même type de données.la source