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

UNPIVOT을 시도하면 FirstnameLastname열이 다음과 유사합니다.

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

SQL Server는 오류를 생성합니다.

메시지 8167, 수준 16, 상태 1, 줄 6

열 "성"유형이 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 참조

UNPIVOT이 SQL Server 2005에 도입되기 전에 SELECTwith UNION ALL를 사용 하여 firstname/ lastname열 을 피벗 해제 하고 열을 동일한 길이로 변환하지 않고도 쿼리를 실행했습니다.

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

Demo with SQL Fiddle을 참조하십시오 .

또한 CROSS APPLY데이터 유형에서 길이가 동일하지 않은 데이터를 사용하여 데이터를 성공적으로 피봇 해제 할 수 있습니다 .

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

Demo with SQL Fiddle을 참조하십시오 .

MSDN을 읽었지만 데이터 유형의 길이를 동일하게 강제하는 이유를 설명하는 것을 찾지 못했습니다.

UNPIVOT을 사용할 때 동일한 길이를 요구하는 논리는 무엇입니까?


4
(아마 관련이 없지만 ...) 재귀 CTE의 두 부분의 열 유형을 비교할 때 동일한 엄격 성이 적용됩니다.
Andriy M

답변:


25

UNPIVOT을 사용할 때 동일한 길이를 요구하는 논리는 무엇입니까?

이 질문은의 구현 작업을 수행 한 사람들 만 진정으로 대답 할 수 있습니다 UNPIVOT. 지원을 받기 위해 연락 하여 얻을 수 있습니다 . 다음은 추론에 대한 나의 이해이며 100 % 정확하지 않을 수 있습니다.


T-SQL에는 여러 가지 이상한 의미론 및 기타 반 직관적 인 동작이 포함되어 있습니다. 이 중 일부는 더 이상 사용 중단주기의 일부로 사라지지만 다른 일부는 절대 '개선'되거나 '고정'되지 않을 수 있습니다. 다른 동작과는 별도로, 이러한 동작에 의존하는 응용 프로그램이 존재하므로 이전 버전과의 호환성을 유지해야합니다.

암시 적 변환 규칙 및 식 유형 파생은 위에서 언급 한 이상한 정도의 상당 부분을 차지합니다. 나는 SET새 버전에 대해 (세션 값 의 모든 조합 등에서) 이상한 (그리고 종종 문서화되지 않은) 행동이 유지되도록 해야하는 테스터를 부러워하지 않습니다 .

즉, 새로운 언어 기능을 도입 할 때 (이전 버전과의 호환성이없는 수하물) 개선하지 않고 과거의 실수를 피할만한 충분한 이유가 없습니다. 재귀 공통 테이블 표현식 ( 설명에서 Andriy M이 언급 한 것과 같은)과 같은 새로운 기능 UNPIVOT은 비교적 정상적인 의미론과 명확하게 정의 된 규칙을 가질 수있었습니다.

유형에 길이를 포함시키는 것이 명시적인 타이핑을 너무 많이 받고 있는지에 대한 다양한 견해가 있지만 개인적으로 환영합니다. 내 관점에서, 유형 varchar(25)varchar(50)있습니다 하지 더 이상 동일 decimal(8)하고 decimal(10)있습니다. 특수 케이스 문자열 유형 변환은 불필요하게 일을 복잡하게 만들고 실제 가치를 추가하지 않습니다.

데이터를 잃을 수있는 암시 적 변환 만 명시 적으로 명시해야한다고 주장 할 수 있지만, 그에 대한 대소 문자도 있습니다. 궁극적으로 변환이 필요할 것이므로 명시 적으로 만들 수도 있습니다.

에서 varchar(25)로의 암시 적 변환 varchar(50)이 허용 된 경우 이는 일반적인 모든 이상한 경우와 SET민감도를 설정하는 또 다른 (가장 숨겨져있는) 암시 적 변환 일뿐 입니다. 구현을 가장 단순하고 가장 명백하게 만들 수없는 이유는 무엇입니까? (아무것도하지만, 완벽, 그것은 수치입니다 숨어 varchar(25)varchar(50)돌며 sql_variant허용됩니다.)

을 다시 쓰기 UNPIVOTAPPLY하고 UNION ALL규칙이 있기 때문에 (더 나은) 타입의 동작을 방지 UNION이전 버전과의 호환성에 따라, 그리고 (암시 적 변환을 사용하므로 그들이 비교할으로 다른 유형을 허용으로 온라인에 설명되어있는 데이터 형식 우선 순위의 비밀의 규칙 사용되는 등).

이 문제를 해결하려면 데이터 형식에 대해 명시 적으로 설명하고 필요한 경우 명시 적 변환을 추가해야합니다. 이것은 나에게 진보처럼 보인다 :)

명시 적으로 형식화 된 해결 방법을 작성하는 한 가지 방법 :

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질문에 사용한 다시 쓰기 는 속성을 UNPIVOT거부하지 않기 때문에 와 완전히 동일 하지 않습니다 NULL.


1

UNPIVOT운전자가 이용하는 IN오퍼레이터. IN 연산자 (아래 스크린 샷) 의 사양test_expression(이 경우 왼쪽 IN) 및 각 expression오른쪽 ( IN)이 동일한 데이터 유형이어야 함을 나타냅니다. 동일한 전이 속성으로 인해 각 표현식의 데이터 유형도 동일해야합니다.

여기에 이미지 설명을 입력하십시오


그렇습니다. 데이터 유형 요구 사항을 이해하지만 문제는 길이가 동일한 이유입니다.
Taryn

나는 간과했고 IN 연산자는 일반적으로 길이에 신경 쓰지 않습니다.
dev_etter

: 당신이 길이를 지정의 필요성을 간과 할 수있는 대안은 SQL_VARIANT 각 캐스팅하는 것입니다 sqlfiddle.com/#!3/13b9a/2/0을
dev_etter
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.