SQL Server 2008 빈 문자열 대 공백


82

나는 오늘 아침에 약간 이상한 일을 만났고 해설을 위해 제출할 것이라고 생각했습니다.

누군가 SQL 2008에 대해 실행할 때 다음 SQL 쿼리가 '동일'을 인쇄하는 이유를 설명 할 수 있습니까? db 호환성 수준은 100으로 설정되어 있습니다.

if '' = ' '
    print 'equal'
else
    print 'not equal'

그리고 이것은 0을 반환합니다.

select (LEN(' '))

공간을 자동 트리밍하는 것 같습니다. 이전 버전의 SQL Server에서 이런 경우인지 알 수 없으며 더 이상 테스트 할 수 없습니다.

프로덕션 쿼리가 잘못된 결과를 반환했기 때문에이 문제가 발생했습니다. 이 동작은 문서화 된 곳에서 찾을 수 없습니다.

누구든지 이것에 대한 정보가 있습니까?


2
SQL 2005 : select len ​​( '')은 0을 반환합니다
Mayo

1
Sql Server 2000에서도 동일합니다.
Pierre-Alain Vigeant

1
이것은 흥미로운 질문입니다. 일치 여부에 관계없이 문자열에 공백을 몇 개 넣어도 동일하게 반환되는 것 같습니다. 더 많은 실험 후 비교 전에 동등 연산자의 양쪽에서 RTRIM을 효과적으로 수행하고 있음을 알았습니다. LEN 함수에 대한 답을 얻은 것 같지만 질문의 평등 부분에 대해 "varchars and equality are thorny in TSQ"보다 더 철저한 답변에 정말 관심이 있습니다.
JohnFx

오라클도 그렇게한다고 믿습니다.
quillbreaker 09-09-09

일반적으로 빈 문자열을 저장하는 것은 나쁜 생각이며 이것이 그 이유 중 하나입니다. 나는 Null 사용을 선호하고 사람들이 null 정보를 빈 문자열이나 정상 범위를 벗어난 데이터와 같은 값으로 만들려고 할 때 많은 문제를 발견합니다.
HLGEM

답변:


87

varchars와 평등은 TSQL에서 까다 롭습니다. 이 LEN기능은 다음과 같이 말합니다.

후미 공백을 제외한 주어진 문자열 표현식의 바이트 수가 아닌 문자 수를 반환합니다 .

문제가되는 데이터 DATALENGTH의 실제 byte개수 를 얻으려면 을 사용해야 합니다 . 유니 코드 데이터가있는 경우이 상황에서 얻는 값은 텍스트 길이와 동일하지 않습니다.

print(DATALENGTH(' ')) --1
print(LEN(' '))        --0

식의 동등성에 관해서는 다음과 같이 두 문자열이 동등성을 비교합니다.

  • 더 짧은 문자열 가져 오기
  • 길이가 긴 문자열과 같아 질 때까지 공백으로 채 웁니다.
  • 두 가지 비교

예상치 못한 결과를 초래하는 중간 단계입니다. 그 단계 후에는 공백과 공백을 효과적으로 비교하므로 동일한 것으로 보입니다.

LIKE=일치시키려는 패턴에서 공백 채우기를 수행하지 않기 때문에 "공백"상황에서 보다 더 잘 작동합니다 .

if '' = ' '
print 'eq'
else
print 'ne'

줄 것입니다 eq:

if '' LIKE ' '
print 'eq'
else
print 'ne'

줄게 ne

주의 LIKE: 대칭 적이 지 않습니다. 후행 공백을 패턴 (RHS)에서 중요한 것으로 처리하지만 일치 표현식 (LHS)은 처리하지 않습니다. 다음은 여기 에서 가져온 것입니다 .

declare @Space nvarchar(10)
declare @Space2 nvarchar(10)

set @Space = ''
set @Space2 = ' '

if @Space like @Space2
print '@Space Like @Space2'
else
print '@Space Not Like @Space2'

if @Space2 like @Space
print '@Space2 Like @Space'
else
print '@Space2 Not Like @Space'

@Space Not Like @Space2
@Space2 Like @Space

1
좋은 대답입니다. 나는 LEN 문서에서 그것을 알아 차리지 못했습니다. 하지만 LEN에 국한되지 않습니다. RIGHT 및 LEFT 함수는 유사한 동작을 나타내지 만 문서화되지 않았습니다. 문제를 일으키는 공백이있는 리터럴 인 것 같습니다. 나는 이것이 또한 equal을 반환한다는 것을 알았습니다. 열, 빈 문자열이었던 모든 열이 반환되었습니다.
jhale 09.09.09

또한 LIKE 문에 대한 좋은 정보입니다. 이야기의 교훈은 공백과 빈 문자열을 비교해야하는 위치에 있지 않도록하는 것입니다.
jhale 09.09.09

2
문제는 공백을 빈 문자열과 비교하는 것보다 큽니다. 다른 수의 공백으로 끝나는 두 문자열을 비교하면 동일한 동작이 나타납니다.
JohnFx 09.09.09

3
@butterchicken : 죄송합니다 같은 늦은 게시물에 대한, 난 그냥이 질문을했다,하지만 난 내에서이 (마지막)을 실행했을 때 sql-server-2008 r2내가 얻을 @Space Not Like @Space2 @Space2 Not Like @Space . 왜 그런지 아세요?
Razort4x 2013

1
SQL Server 2012 및 SQL Server 2014에서 확인 된 결과는@Space Not Like @Space2 @Space2 Not Like @Space
Just a learner

19

= 연산자는 T-SQL이 "표현식 컨텍스트의 데이터 정렬에 따라 동일한 단어 / 구문"이기 때문에 "같지 않음"이고 LEN은 "단어 / 구문의 문자 수"입니다. 데이터 정렬은 후행 공백을 선행하는 단어 / 구문의 일부로 처리하지 않습니다 (단, 선행 공백을 선행하는 문자열의 일부로 처리 함).

'this'와 'this'를 구분해야하는 경우 'this'와 'this'는 같은 단어이므로 "같은 단어 또는 구문입니다"연산자를 사용하지 마십시오.

= works 방식에 기여하는 것은 문자열 같음 연산자가 인수의 내용과 표현식의 데이터 정렬 컨텍스트에 의존해야한다는 생각이지만, 둘 다 문자열 유형 인 경우 인수의 유형에 의존해서는 안됩니다. .

"이것들은 같은 단어입니다"라는 자연어 개념은 일반적으로 =와 같은 수학적 연산자로 캡처 할 수있을만큼 정확하지 않으며 자연어에는 문자열 유형 개념이 없습니다. 문맥 (즉, 대조)은 중요하고 (자연어로 존재) 스토리의 일부이며, 추가 속성 (일부 기발 해 보이는)은 =의 부 자연스러운 세계에서 잘 정의되도록 정의의 일부입니다. 데이터.

유형 문제에서 단어가 다른 문자열 유형에 저장 될 때 변경되는 것을 원하지 않을 것입니다. 예를 들어, VARCHAR (10), CHAR (10) 및 CHAR (3) 유형은 모두 'cat'및?라는 단어의 표현을 보유 할 수 있습니다. = 'cat'은 이러한 유형의 값이 'cat'이라는 단어를 포함하는지 여부를 결정하도록해야합니다 (대소 문자 및 악센트 문제는 데이터 정렬에 의해 결정됨).

JohnFx의 의견에 대한 응답 :

보다 온라인 설명서에서 char 및 varchar 데이터 사용을 . 그 페이지에서 인용하면 다음과 같습니다.

각 char 및 varchar 데이터 값에는 데이터 정렬이 있습니다. 데이터 정렬은 각 문자를 나타내는 데 사용되는 비트 패턴과 같은 속성을 정의합니다. 비교 규칙 및 대소 문자 또는 악센트에 대한 민감도.

찾기가 더 쉬울 수 있다는 데 동의하지만 문서화되어 있습니다.

또한 주목할 가치가있는 것은 =가 실제 데이터와 관련이있는 SQL의 의미 체계와 비교 컨텍스트 (컴퓨터에 저장된 비트에 대한 내용과 반대)가 오랫동안 SQL의 일부 였다는 것입니다. RDBMS와 SQL의 전제는 실제 데이터를 충실하게 표현하는 것이므로 유사한 아이디어 (예 : CultureInfo)가 Algol과 유사한 언어 영역에 진입하기 수년 전에 데이터 정렬을 지원합니다. 이러한 언어의 전제는 비즈니스 데이터 관리가 아니라 엔지니어링 문제 해결이었습니다. (최근에는 검색과 같은 비 엔지니어링 애플리케이션에서 유사한 언어를 사용하는 것이 일부 확산되고 있지만 Java, C # 등은 여전히 ​​비업무 적 뿌리에서 어려움을 겪고 있습니다.)

제 생각에는 SQL이 "대부분의 프로그래밍 언어"와 다르다고 비판하는 것은 공정하지 않습니다. SQL은 엔지니어링과는 매우 다른 비즈니스 데이터 모델링을위한 프레임 워크를 지원하도록 설계되었으므로 언어가 다르며 목표에 더 적합합니다.

SQL이 처음 지정되었을 때 일부 언어에는 기본 제공 문자열 유형이 없었습니다. 그리고 일부 언어에서는 여전히 문자열 사이의 같음 연산자가 문자 데이터를 전혀 비교하지 않고 참조를 비교합니다! 앞으로 10 ~ 20 년 후에 ==가 문화에 의존한다는 생각이 표준이된다면 놀라지 않을 것입니다.


BOL은 = 연산자를 다음과 같이 설명합니다. "두 표현식 (비교 연산자)의 동등성을 비교합니다." 동작이 올바른지 여부에 관계없이 대부분의 프로그래밍 언어에서이 연산자를 사용하는 측면에서 매우 혼란스럽고 비표준임을 인정해야합니다. MS는 최소한이 동작에 대한 경고를 문서에 추가해야합니다.
JohnFx

@JohnFx : 내 대답에 너무 긴 댓글이 없습니다.
Steve Kass

9

나는 행동을 설명하고 그 이유를 설명하는 이 블로그 기사 를 찾았습니다 .

SQL 표준에서는 문자열 비교를 통해 더 짧은 문자열을 공백 문자로 효과적으로 채워야합니다. 이로 인해 N ''= N ''(빈 문자열은 하나 이상의 공백 문자로 구성된 문자열과 동일)이라는 놀라운 결과가 발생하고, 후행 공백 만 다른 경우 더 일반적으로 모든 문자열은 다른 문자열과 같습니다. 이는 일부 상황에서 문제가 될 수 있습니다.

MSKB316626 에서도 더 많은 정보를 볼 수 있습니다.


감사. 나는 그것이 표준에 있다는 것에 놀랐습니다. 나는 나보다 훨씬 더 똑똑한 사람이 이것에 대한 좋은 이유가 있다고 확신합니다.
jhale 09.09.09

@John : 댓글에 ≠ (같지 않음)이라고 쓰려고 했나요?
Steve Kass

원래 견적에는 내가 직접 복사 한 오류가 있습니다. 원저자가 의미하는 바를 반영하기 위해 인용문을 업데이트했습니다.
JohnFx 09.09.09

5

얼마 전에 비슷한 문제를 여기에서 조사한 비슷한 질문이있었습니다.

대신 올바른 값을 제공 LEN(' ')하는 DATALENGTH(' ')-를 사용 하십시오.

해결책은 LIKE거기에 내 대답에 설명 된 절 을 사용 하고 / 또는 WHERE절에 두 번째 조건을 포함 하여 확인하는 것이 었습니다.DATALENGTH 습니다.

그 질문과 링크를 읽으십시오.


3

값을 리터럴 공간과 비교하려면 LIKE 문 대신이 기술을 사용할 수도 있습니다.

IF ASCII('') = 32 PRINT 'equal' ELSE PRINT 'not equal'

0

SQL Server에서 char / varchar 필드를 사용하여 선택시 레코드를 구별하는 방법 : 예 :

declare @mayvar as varchar(10)

set @mayvar = 'data '

select mykey, myfield from mytable where myfield = @mayvar

예상

mykey (int) | myfield (varchar10)

1 | '데이터'

획득

mykey | Myfield

1 | '데이터' 2 | '데이터'

내가 써도 select mykey, myfield from mytable where myfield = 'data'(마지막 공백없이) 같은 결과를 얻습니다.

내가 어떻게 해결 했어? 이 모드에서 :

select mykey, myfield
from mytable
where myfield = @mayvar 
and DATALENGTH(isnull(myfield,'')) = DATALENGTH(@mayvar)

myfield에 인덱스가있는 경우 각 경우에 사용됩니다.

도움이 되었으면합니다.


0

또 다른 방법은 공간에 가치가있는 상태로 되 돌리는 것입니다. 예 : 공백을 _와 같은 문자로 대체하십시오.

if REPLACE('hello',' ','_') = REPLACE('hello ',' ','_')
    print 'equal'
else
    print 'not equal'

반환 : 같지 않음

이상적이지 않고 느릴 수도 있지만 빠르게 필요할 때 앞으로 나아가는 또 다른 빠른 방법입니다.


0

때로는 Null을 사용하는 것이 더 낫지 만 항상 사용 가능한 것은 아니지만 다른 문자가 있든 없든 데이터의 공백을 처리해야합니다. 나는 설명 된 상황에 부딪 쳤고 다음과 같이 해결했습니다.

... where ('>' + @space + '<') <> ('>' + @space2 + '<')

물론 많은 양의 데이터에 대해서는 그렇게하지 않을 것이지만 수백 줄에 대해 빠르고 쉽게 작동합니다.


1
문제는 SQL 서버가 일반적으로 이러한 동작을 처리하는 방법이 아니라 그랬던 것처럼 동작하는 이유 였습니다 . jhale은 아마도 그의 프로그램 코드를 수정하지 않고 오직 그의 서버 구성만을 수정할 것입니다.
Lutz Prechelt 2015 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.