UNPIVOTを使用する場合、SQL Serverでデータ型の長さが同じである必要があるのはなぜですか?


28

UNPIVOT正規化されていないデータに関数を適用する場合、SQL Serverではデータ型と長さが同じである必要があります。データ型が同じでなければならない理由を理解していますが、なぜUNPIVOTは同じ長さを必要とするのですか?

ピボットを解除する必要がある次のサンプルデータがあるとします。

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');

次のような列FirstnameLastname列をアンピボットしようとすると:

select PersonId, ColumnName, Value  
from People
unpivot
(
  Value 
  FOR ColumnName in (FirstName, LastName)
) unpiv;

SQL Serverはエラーを生成します。

メッセージ8167、レベル16、状態1、行6

列「Lastname」のタイプは、UNPIVOTリストで指定された他の列のタイプと競合します。

エラーを解決するには、サブクエリを使用して最初にLastname列をキャストし、次と同じ長さにする必要があります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;

SQL Fiddle with Demoをご覧ください

SQL Server 2005でUNPIVOTが導入される前は、SELECTwith UNION ALLを使用してfirstname/ lastname列のピボットを解除すると、列を同じ長さに変換する必要なくクエリが実行されました。

select personid, 'firstname' ColumnName, firstname value
from People
union all
select personid, 'LastName', LastName
from People;

SQL Fiddle with Demoを参照してください。

またCROSS APPLY、データ型の長さが同じでなくても、データを正常にアンピボットできます。

select PersonId, columnname, value
from People
cross apply
(
    select 'firstname', firstname union all
    select 'lastname', lastname
) c (columnname, value);

SQL Fiddle with Demoを参照してください。

MSDNを読みましたが、データ型の長さを強制的に同じにする理由を説明するものが見つかりませんでした。

UNPIVOTを使用する際に同じ長さを要求する理由は何ですか?


4
(おそらく無関係ですが...)再帰CTEの2つの部分の列タイプを比較する場合、同じ厳密さが適用されます。
アンドリーM

回答:


25

UNPIVOTを使用する際に同じ長さを要求する理由は何ですか?

この問題はの実装に取り​​組んだ人々によって本当に答えられるかもしれませんUNPIVOTサポートを受けるには、連絡してください。以下は推論の私の理解であり、100%正確ではないかもしれません:


T-SQLには、奇妙なセマンティクスやその他の直感に反する動作のインスタンスがいくつでも含まれています。これらのいくつかは、非推奨サイクルの一部として最終的に消滅しますが、その他は「改善」または「修正」されることはありません。他のこととは別に、これらの動作に依存するアプリケーションが存在するため、下位互換性を維持する必要があります。

暗黙の変換のルールと式の型の導出は、上記の奇妙さのかなりの部分を占めています。SET新しいバージョンの奇妙な(そして文書化されていないことが多い)動作が(セッション値のすべての組み合わせなどで)維持されることを保証する必要があるテスターをtheましく思いません。

とは言っても、新しい言語機能を導入する際には、改善を行わず、過去の間違いを避ける正当な理由はありません(明らかに後方互換性の手荷物はありません)。再帰的な共通テーブル式(Andriy Mがコメントで述べているように)などの新機能と、UNPIVOT比較的健全なセマンティクスと明確に定義されたルールを自由に持つことができました。

型に長さを含めることで明示的な型付けが行き過ぎているかどうかについてはさまざまな意見がありますが、個人的には歓迎します。私の見解では、種類varchar(25)varchar(50)されていない、任意のより多くのと同じdecimal(8)decimal(10)されています。私の意見では、特殊なケーシング文字列型変換は不必要に物事を複雑にし、真の価値を追加しません。

データを失う可能性のある暗黙的な変換のみを明示的に指定する必要があると主張することができますが、そこにはエッジケースもあります。最終的には変換が必要になるので、明示的にすることもできます。

以下からの暗黙的な変換した場合varchar(25)には、varchar(50)許された、それだけですべての通常の奇妙なエッジケースとで別の(最も可能性が高い隠された)暗黙の変換、だろうSET設定感度。実装を可能な限り最も単純で最も明示的なものにしませんか?(何もしかし、完璧ではありません、それが隠れていること恥であるvarchar(25)varchar(50)内部にsql_variant許可されています。)

UNPIVOTwith APPLYを書き直し、UNION ALL(より良い)型の動作を回避します。これは、ルールUNIONが下位互換性の対象であり、暗黙の変換を使用して比較できる限り異なるタイプを許可するようにBooks Onlineに文書化されているためです(データ型優先の不可解なルールなどが使用されます)。

回避策には、データ型を明示的に指定し、必要に応じて明示的な変換を追加することが含まれます。これは私には進歩のように見える:)

明示的に型指定された回避策を記述する1つの方法:

SELECT
    U.PersonId,
    U.ColumnName,
    U.Value
FROM dbo.People AS P
CROSS APPLY
(
    VALUES (CONVERT(varchar(50), Lastname))
) AS CA (Lastname)
UNPIVOT
(
    Value FOR
    ColumnName IN (P.Firstname, CA.Lastname)
) AS U;

再帰CTEの例:

-- Fails
WITH R AS
(
    SELECT Dummy = 'A row'
    UNION ALL
    SELECT 'Another row'
    FROM R
    WHERE Dummy = 'A row'
)
SELECT Dummy
FROM R;

-- Succeeds
WITH R AS
(
    SELECT Dummy = CONVERT(varchar(11), 'A row')
    UNION ALL
    SELECT CONVERT(varchar(11), 'Another row')
    FROM R
    WHERE Dummy = 'A row'
)
SELECT Dummy
FROM R;

最後CROSS APPLYに、質問で使用する書き換えはUNPIVOTNULL属性を拒否しないため、とまったく同じではないことに注意してください。


1

UNPIVOTオペレータは、利用IN演算子を。IN演算子仕様(下のスクリーンショット)は、test_expression(この場合はの左側IN)とそれぞれexpression(の右側)の両方INが同じデータ型でなければならないことを示しています。等しいという推移的な特性のおかげで、各式も同じデータ型でなければなりません。

ここに画像の説明を入力してください


確かに、データ型の要件は理解していますが、問題は長さが同じでなければならない理由です。
タリン

私はそれを見落としていました。そして、はい、IN演算子は通常長さを気にしません。
dev_etter

長さを指定する必要性を見落とすことを可能にする代替策は、それぞれをSQL_Variantとしてキャストすることです。sqlfiddle.com/ #
dev_etter
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.