테이블 행에서 "CO2"를 "CO₂"로 업데이트 할 수 없습니다


19

이 테이블이 주어지면 :

CREATE TABLE test (
    id INT NOT NULL,
    description NVARCHAR(100) COLLATE Modern_Spanish_CI_AS NOT NULL
);
INSERT INTO test (id, description) VALUES (1, 'CO2');

인쇄상의 문제를 해결할 수 없다는 것을 깨달았습니다.

SELECT * FROM test WHERE id = 1;
UPDATE test SET description = 'CO₂' WHERE id = 1;
SELECT * FROM test WHERE id = 1;

업데이트가 일치하지만 효과가 없기 때문에 :

id          description
----------- -----------
1           CO2

(1 affected rows)

(1 affected rows)

id          description
----------- -----------
1           CO2

(1 affected rows)

마치 SQL Server 가 가 단지 2 이기 때문에 최종 값은 변경되지 않으므로 변경할 가치가 없다고 결정하는 것처럼 SQL Server가 결정 합니다.

누군가 이것에 약간의 빛을 비추고 대안을 제안 할 수 있습니까 (중간 값으로 업데이트하는 것 이외)?


1
Álvaro :이 동작에 대해 더 배우고 싶다면 왜 이런 일이 발생했는지 더 잘 이해하기 위해 방금 추가 한 두 개의 링크를 참조하십시오.
Solomon Rutzky

답변:


29

아래 첨자 2는 varchar 문자 세트의 일부가 아닙니다 (Modern_Spanish뿐만 아니라 모든 데이터 정렬에서). 따라서 nvarchar 상수로 만드십시오.

UPDATE test SET description = N'CO₂' WHERE id = 1;

1
나는 그 가치를 고쳤을뿐만 아니라, 그것이 처음에 어떻게 도착했는지 이해했습니다. 감사합니다!
Álvaro González

2
@ ÁlvaroGonzález와 gbn : 문제의 데이터베이스의 기본 데이터 정렬에 의해 지정된 코드 페이지에서 "아래 첨자 2"를 사용할 수 없습니다. 이는 열의 데이터 정렬이 아닌 문자열 리터럴 및 변수에 사용되는 데이터 정렬입니다. 동일한 코드 페이지를 사용할 수 있습니다). 그러나 "첨자 2"는 한국어 데이터 정렬을 통해 코드 페이지 949에서 사용할 수 있습니다. 그것은 여기에 도움이되지 않지만 단지 참고하십시오. 내 답변 에 세부 사항과 예가 있습니다.
솔로몬 루츠 키

21

@gbn은 이미 기본 이유와 수정을 설명했지만 현재보고있는 동작의 구체적인 이유는 다음과 같습니다.

  1. 당신은 사용하는 VARCHAR문자 (NO N접두사) 대신의 NVARCHAR문자 (와 문자열 N접두어), 따라서 유니 코드 문자로 변환되는 것이다 VARCHAR.
  2. VARCHAR대부분의 경우 문자 당 1 바이트이지만 문자 당 2 바이트 일 수있는 8 비트 인코딩입니다. 반면에 NVARCHAR문자 당 2 바이트 또는 4 바이트 인 16 비트 인코딩 (UTF-16 Little Endian)입니다.
  3. 문자를 매핑하는 데 사용할 수있는 바이트 수의 차이로 인해 8 비트 인코딩은 그 특성상 매핑 할 수있는 문자 수가 훨씬 더 제한됩니다. VARCHAR데이터는 1 바이트 문자 세트 (대부분)의 경우 최대 256 자이고 2 바이트 문자 세트 (대부분의 숫자)의 경우 최대 65,536 자입니다. 반면, NVARCHAR데이터는 110 만 개 이상의 유니 코드 문자를 매핑 할 수 있습니다 (현재 매핑 된 250k 미만).
  4. 8 비트 / VARCHAR데이터 로 수행 할 수있는 매핑 수가 제한되어 있기 때문에 (언어 / 문화를 기반으로하는) 문자 그룹이 여러 "코드 페이지"(예 : 문자 세트)에 분산됩니다.
  5. 각 데이터 정렬은 데이터에 사용할 코드 페이지 (있는 경우)를 지정 VARCHAR합니다 ( NVARCHAR모든 문자 임)
  6. 문자열 리터럴 또는 변수를 NVARCHAR(예 : 유니 코드 / UTF-16 / 모든 문자)에서 VARCHAR(대부분의 데이터 정렬에 지정된 코드 페이지를 기반으로하는 문자 세트 )로 변환 할 때 데이터베이스의 기본 데이터 정렬이 사용됩니다
  7. 변환에 사용되는 데이터 정렬의 코드 페이지에 동일한 문자가 없지만 "최 적합"매핑이 포함 된 경우 "최 적합"매핑이 사용됩니다.
  8. 변환에 사용되는 데이터 정렬의 코드 페이지에 동일한 문자가 없거나 "가장 적합"매핑이 포함 된 경우 기본 "대체"문자가 사용됩니다 (가장 일반적으로 ?).

그래서, 당신이보고있는 것은입니다 NVARCHARVARCHAR인해 누락 변환을 N문자열 리터럴에 접두사. 그리고 데이터베이스에 대한 기본 데이터 정렬의 코드 페이지에 정확히 동일한 문자가 포함되어 있지 않지만 "최적의"매핑이 발견 되었기 때문에 . 2대신을 (를) 받고 ?있습니다.

다음과 같은 간단한 테스트를 수행하여이 효과를 볼 수 있습니다.

SELECT '₂', N'₂';

보고:

2    ₂

분명히 데이터베이스의 기본 데이터 정렬의 코드 페이지에 정확히 동일한 문자가 포함되어 있으면 해당 코드 페이지에서 동일한 문자로 변환 된 것입니다. 그런 다음 NVARCHAR열에 저장 하기 때문에 원래의 유니 코드 문자로 다시 변환되었을 것입니다. 아래의 마지막 예는이 동작을 보여줍니다.

중요 : 문자열 리터럴을 해석 할 때 열에 저장 되기 전에 변환이 수행됩니다. 이는 열이 해당 문자를 보유 할 수있는 경우에도 N해당 문자열 리터럴의 접 두부 를 생략하여 데이터베이스의 기본 데이터 정렬을 기반으로 이미 다른 문자로 변환되었음을 의미합니다. 그리고 이것은 정확히 당신이 경험 한 것입니다.

예를 들어, 데이터베이스의 기본 데이터 정렬이 한국어 데이터 정렬 중 하나 (4 개의 2 바이트 문자 집합 중 하나) 인 경우 해당 문자에서 "아래 첨자 2"문자를 사용할 수 있으므로이 문제를 보지 못했을 것입니다. 설정 (코드 페이지 949). 다음 테스트를 수행하여 확인하십시오 (데이터베이스의 기본 데이터 정렬 대신 열의 데이터 정렬을 사용하는 것이 더 쉽습니다).

CREATE TABLE #TestChar
(
    [8bit_Latin1_General-1252] VARCHAR(2) COLLATE Latin1_General_100_CI_AS_SC,
    [8bit_Korean-949] VARCHAR(2) COLLATE Korean_100_CI_AS_SC,
    [UTF16LE_Latin1_General-1252] NVARCHAR(2) COLLATE Latin1_General_100_CI_AS_SC
);

INSERT INTO #TestChar VALUES (N'₂', N'₂', N'₂');

SELECT * FROM #TestChar;

보고:

8bit_Latin1_General-1252    8bit_Korean-949    UTF16LE_Latin1_General-1252
2                           ₂                  ₂

보다시피, 데이터에 Code Page 1252 ( Modern_SpanishCollations에서 사용 하는 것과 동일한 Code Page)를 사용하는 Latin1_General Collations VARCHAR는 정확히 일치하지는 않지만 " 최 적합"매핑 (현재보고있는 것)을 갖습니다. ). 그러나 데이터에 코드 페이지 949를 사용하는 한국어 데이터 정렬 VARCHAR은 "아래 첨자 2"문자와 정확히 일치합니다.


더 자세히 설명하기 위해 한국어 데이터 정렬 중 하나의 기본 데이터 정렬을 사용하여 새 데이터베이스를 만든 다음 질문에있는 정확한 SQL을 실행할 수 있습니다.

CREATE DATABASE [TestKorean-949] COLLATE Korean_100_CI_AS_KS_WS_SC;
ALTER DATABASE [TestKorean-949] SET RECOVERY SIMPLE;
GO

USE [TestKorean-949];

CREATE TABLE test (
    id INT NOT NULL,
    description NVARCHAR(100) COLLATE Modern_Spanish_CI_AS NOT NULL
);
INSERT INTO test (id, description) VALUES (1, 'CO2');


SELECT * FROM test WHERE id = 1;
UPDATE test SET description = 'CO₂' WHERE id = 1;
SELECT * FROM test WHERE id = 1;

보고:

id  description
1   CO2


id  description
1   CO₂

최신 정보

여기에서 정확히 무슨 일 이 일어나고 있는지에 대해 더 자세히 알고 싶은 사람은 방금 게시 한 두 부분으로 된 조사를 참조하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.