SQL Server에서 선행 0을 트리밍하는 더 나은 기술은 무엇입니까?


161

내가 사용했던 시간을 위해 :

SUBSTRING(str_col, PATINDEX('%[^0]%', str_col), LEN(str_col))

그러나 최근에는 '0'이 아닌 문자가 일치하지 않기 때문에 '00000000'과 같은 모든 "0"문자가있는 열에서 문제를 발견했습니다.

내가 본 대안 기술은 다음과 TRIM같습니다.

REPLACE(LTRIM(REPLACE(str_col, '0', ' ')), ' ', '0')

공백이 다시 "0"으로 바뀌면 공백이 "0"으로 바뀌기 때문에 임베드 된 공백이 있으면 문제가 있습니다.

스칼라 UDF를 피하려고합니다. SQL Server 2005에서 UDF와 관련된 많은 성능 문제를 발견했습니다.


문자열의 나머지 부분에는 항상 '숫자'문자 만 포함됩니까, 아니면 알파도 포함되어 있습니까? 그것이 단지 숫자 데이터라면, Quassnoi의 정수와 캐스트에 대한 제안은 좋은 것 같습니다.
robsoft 2014 년

일반적인 기술입니다. 이들은 일반적으로 부적합한 필드에 나오는 계좌 번호이며, 데이터웨어 하우스가 ETL에서 사용하는 적합성 규칙과 일치하는지 확인해야합니다 (물론 훨씬 더 완전한 기능을 갖춘 SSIS 환경에서 사용한다고 가정합니다). 트림 스타트).
Cade Roux

답변:


283
SUBSTRING(str_col, PATINDEX('%[^0]%', str_col+'.'), LEN(str_col))

2
영리한 생각 이었으면 좋겠다.
Cade Roux

4
신경 쓰지 마, 나는 '.' 패턴을 찾기 위해서만 사용되기 때문에 하위 문자열에 없습니다-생각보다 영리합니다.
Cade Roux

2
이것을 함수로 캡슐화하면 쿼리 속도가 느려졌습니다. 왜 그런지 잘 모르겠지만 형식 변환과 관련이 있다고 생각합니다. SUBSTRING 인라인을 사용하는 것이 훨씬 빨랐습니다.
Ronnie Overby

1
질문은 이것에 대한 문제는 0을 구문 분석 할 때 공백을 얻는 것입니다. '0'값과 공백 값의 차이를 알 수 있어야합니다. 전체 솔루션에 대한 내 게시물을 참조하십시오 stackoverflow.com/a/21805081/555798
MikeTeeVee

1
@Arvo Wow ... 잠시 동안 나는 혼란스러워서 나를 도울 수있는이 질문에 대답했다고 생각했습니다. 나는 처음에 또 다른 것을 보았다 Arvo!
Arvo Bowen

41

왜 값을 캐스트 INTEGER한 다음에 다시 돌려 보내지 VARCHAR않겠습니까?

SELECT  CAST(CAST('000000000' AS INTEGER) AS VARCHAR)

--------
       0

11
문자열 열이므로 때때로 숫자가 아닌 데이터를 기대하고 있습니다. 데이터가 대부분 숫자 인 MRN 번호와 같은 것 .
Joel Coehoorn

1
불행히도 숫자 데이터에만 작동하며 때로는 문자열도 정수 범위를 초과하므로 bigint를 사용해야합니다.
Cade Roux

3
SELECT CASE ISNUMERIC(str_col) WHEN 1 THEN CAST(CAST(str_col AS BIGINT) AS VARCHAR(255)) ELSE str_col END
Yuriy Rozhovetskiy

로도 BIGINT일부 유형의 문자열은 여전히이 변환에 실패합니다. 고려 0001E123예를 들어.
roaima

1
내 테스트 (및 경험)에서 허용되는 답변과 비교할 때 상대적으로 비용이 많이 드는 작업입니다. 성능상의 이유로 데이터 유형을 변경하거나 다른 유형의 데이터를 비교할 수없는 경우 데이터 유형을 변경하지 않는 것이 가장 좋습니다.
reedstonefood 2015

14

모두 0 (또는 단일 0)이있는 경우 고려하지 않는 다른 대답.
일부는 항상 빈 문자열을 0으로 기본 설정합니다. 빈 문자열로 유지해야 할 때 잘못되었습니다.
원래 질문을 다시 읽으십시오. 이것은 질문자가 원하는 것에 응답합니다.

해결책 # 1 :

--This example uses both Leading and Trailing zero's.
--Avoid losing those Trailing zero's and converting embedded spaces into more zeros.
--I added a non-whitespace character ("_") to retain trailing zero's after calling Replace().
--Simply remove the RTrim() function call if you want to preserve trailing spaces.
--If you treat zero's and empty-strings as the same thing for your application,
--  then you may skip the Case-Statement entirely and just use CN.CleanNumber .
DECLARE @WackadooNumber VarChar(50) = ' 0 0123ABC D0 '--'000'--
SELECT WN.WackadooNumber, CN.CleanNumber,
       (CASE WHEN WN.WackadooNumber LIKE '%0%' AND CN.CleanNumber = '' THEN '0' ELSE CN.CleanNumber END)[AllowZero]
 FROM (SELECT @WackadooNumber[WackadooNumber]) AS WN
 OUTER APPLY (SELECT RTRIM(RIGHT(WN.WackadooNumber, LEN(LTRIM(REPLACE(WN.WackadooNumber + '_', '0', ' '))) - 1))[CleanNumber]) AS CN
--Result: "123ABC D0"

솔루션 # 2 (샘플 데이터 포함) :

SELECT O.Type, O.Value, Parsed.Value[WrongValue],
       (CASE WHEN CHARINDEX('0', T.Value)  > 0--If there's at least one zero.
              AND LEN(Parsed.Value) = 0--And the trimmed length is zero.
             THEN '0' ELSE Parsed.Value END)[FinalValue],
       (CASE WHEN CHARINDEX('0', T.Value)  > 0--If there's at least one zero.
              AND LEN(Parsed.TrimmedValue) = 0--And the trimmed length is zero.
             THEN '0' ELSE LTRIM(RTRIM(Parsed.TrimmedValue)) END)[FinalTrimmedValue]
  FROM 
  (
    VALUES ('Null', NULL), ('EmptyString', ''),
           ('Zero', '0'), ('Zero', '0000'), ('Zero', '000.000'),
           ('Spaces', '    0   A B C '), ('Number', '000123'),
           ('AlphaNum', '000ABC123'), ('NoZero', 'NoZerosHere')
  ) AS O(Type, Value)--O is for Original.
  CROSS APPLY
  ( --This Step is Optional.  Use if you also want to remove leading spaces.
    SELECT LTRIM(RTRIM(O.Value))[Value]
  ) AS T--T is for Trimmed.
  CROSS APPLY
  ( --From @CadeRoux's Post.
    SELECT SUBSTRING(O.Value, PATINDEX('%[^0]%', O.Value + '.'), LEN(O.Value))[Value],
           SUBSTRING(T.Value, PATINDEX('%[^0]%', T.Value + '.'), LEN(T.Value))[TrimmedValue]
  ) AS Parsed

결과 :

MikeTeeVee_SQL_Server_Remove_Leading_Zeros

요약:

선행 제로를 일회성 제거하기 위해 위의 내용을 사용할 수 있습니다.
많이 재사용하려는 경우 ITVF (Inline-Table-Valued-Function)에 배치하십시오.
UDF의 성능 문제에 대한 귀하의 우려는 이해할 수 있습니다.
그러나이 문제는 All-Scalar-Functions 및 Multi-Statement-Table-Function에만 적용됩니다.
ITVF를 사용하는 것은 완벽합니다.

타사 데이터베이스와 동일한 문제가 있습니다.
영숫자 필드를 사용하면 많은 사람들이 선행 공백없이 입력됩니다.
따라서 누락 된 선행 0을 정리하지 않고 결합 할 수 없습니다.

결론:

선행 0을 제거하는 대신 조인을 수행 할 때 잘린 값을 선행 0으로 채우는 것이 좋습니다.
더 좋은 방법은 선행 0을 추가 한 다음 인덱스를 다시 작성하여 테이블의 데이터를 정리하는 것입니다.
나는 이것이 더 빠르고 덜 복잡하다고 생각합니다.

SELECT RIGHT('0000000000' + LTRIM(RTRIM(NULLIF(' 0A10  ', ''))), 10)--0000000A10
SELECT RIGHT('0000000000' + LTRIM(RTRIM(NULLIF('', ''))), 10)--NULL --When Blank.

4
@DiegoQueiroz 만약 답이 틀렸다면, 다운 랭크하고 왜 효과가 없는지 설명하십시오. 답변이 효과가 있지만 너무 포괄적 인 경우이 사이트에서 나와 다른 회원의 순위를 낮추지 마십시오. 의견 감사합니다. 좋은 피드백입니다. 저는 진심으로 말합니다.
MikeTeeVee 2012 년

5

공백 대신 0을 일반적으로 열 텍스트에 포함되지 않아야하는 '희귀 한'공백 문자로 바꿉니다. 줄 바꿈은 아마도 이와 같은 열에 충분할 것입니다. 그런 다음 LTrim을 정상적으로 수행하고 특수 문자를 다시 0으로 바꿀 수 있습니다.


3

문자열이 완전히 0으로 구성된 경우 다음은 '0'을 반환합니다.

CASE WHEN SUBSTRING(str_col, PATINDEX('%[^0]%', str_col+'.'), LEN(str_col)) = '' THEN '0' ELSE SUBSTRING(str_col, PATINDEX('%[^0]%', str_col+'.'), LEN(str_col)) END AS str_col

값에 0이 없으면 (공백) 0을 반환합니다.
MikeTeeVee

왜 str_col + '가 있습니다.' 그리고 str_col뿐만 아니라? 도트는 무엇을합니까?
Muflix

2

이것은 좋은 기능을 만듭니다 ....

DROP FUNCTION [dbo].[FN_StripLeading]
GO
CREATE FUNCTION [dbo].[FN_StripLeading] (@string VarChar(128), @stripChar VarChar(1))
RETURNS VarChar(128)
AS
BEGIN
-- http://stackoverflow.com/questions/662383/better-techniques-for-trimming-leading-zeros-in-sql-server
    DECLARE @retVal VarChar(128),
            @pattern varChar(10)
    SELECT @pattern = '%[^'+@stripChar+']%'
    SELECT @retVal = CASE WHEN SUBSTRING(@string, PATINDEX(@pattern, @string+'.'), LEN(@string)) = '' THEN @stripChar ELSE SUBSTRING(@string, PATINDEX(@pattern, @string+'.'), LEN(@string)) END
    RETURN (@retVal)
END
GO
GRANT EXECUTE ON [dbo].[FN_StripLeading] TO PUBLIC

값에 0이 없으면 (공백) 0을 반환합니다. 이 대답은 또한 위의 질문에 UDF 사용을 피하기 위해 구체적으로 명시되어있을 때 다중 문 스칼라 함수를 사용합니다.
MikeTeeVee

2

문자열이 숫자이면 캐스트 (값은 정수)는 항상 작동합니다


이것은 질문에 대한 답변을 제공하지 않습니다. 저자에게 비평을하거나 설명을 요청하려면 게시물 아래에 의견을 남겨주십시오. - 검토에서
Josip Ivic

1
사실 그것은 작동하기 때문에 대답입니까? 답변은 긴 될 필요가 없습니다
tichra

답이 길어질 필요는 없지만, 가능하면 완전해야하며 답이 맞지 않아야합니다. 결과의 데이터 유형을 변경합니다. 이것이 더 나은 응답 일 것이라고 생각합니다 : SELECT CAST (CAST (value AS Int) AS VARCHAR). 계산 된 값이 2.1x10 ^ 9 (8 자리 제한)를 초과하면 Int에 오류가 발생한다는 점도 언급해야합니다. BigInt를 사용하면 값이 약 19 자리 (9.2x10 ^ 18)를 초과하면 오류가 발생합니다.
J. Chris Compton

2

내 버전은 Arvo의 작업에 대한 적응이며 다른 두 가지 경우를 보장하기 위해 조금 더 추가되었습니다.

1) 0이 모두 있으면 숫자 0을 반환해야합니다.

2) 공백이 있으면 공백 문자를 반환해야합니다.

CASE 
    WHEN PATINDEX('%[^0]%', str_col + '.') > LEN(str_col) THEN RIGHT(str_col, 1) 
    ELSE SUBSTRING(str_col, PATINDEX('%[^0]%', str_col + '.'), LEN(str_col))
 END

1
replace(ltrim(replace(Fieldname.TableName, '0', '')), '', '0')

토마스 G의 제안은 우리의 필요를 위해 일했습니다.

우리의 경우 필드는 이미 문자열이었고 선행 0 만 다듬어야했습니다. 대부분 숫자이지만 때로는 문자가 있으므로 이전 INT 변환이 중단됩니다.


아니, 이것은 심지어 0을 후미로
다듬는 다

1
SELECT CAST(CAST('000000000' AS INTEGER) AS VARCHAR)

INT로 변환 할 수있는 문자열 길이에 제한이 있습니다.


왜 이것이 효과가 있다고 생각하는지에 대한 답을 조금 더 설명해 주시겠습니까? 이것이 0이 많은 0이 아닌 숫자라면 어떻게 될까요?
Taegost

숫자가 18 자리 이하 (및 한계가 실제로 9.2x10 ^ 18이므로 대부분의 19 자리 숫자가 작동하는 경우) SELECT CAST (CAST (@Field_Name AS BigInt) AS VARCHAR)를 사용하여 선행 0을 제거 할 수 있습니다. 참고 : msg 8114 "varchar 데이터 형식을 bigint로 변환하는 동안 오류가 발생했습니다."와 함께 숫자가 아닌 문자 (대시, 문자, 마침표 등)가있는 경우 실패합니다.
J. Chris Compton

1

Snowflake SQL을 사용하는 경우 다음을 사용할 수 있습니다.

ltrim(str_col,'0')

ltrim 함수는 지정된 문자 세트의 모든 인스턴스를 왼쪽에서 제거합니다.

따라서 '00000008A'의 ltrim (str_col, '0')은 '8A'를 반환합니다.

'$ 125.00'의 rtrim (str_col, '0.')은 '$ 125'를 반환합니다.


1
  SUBSTRING(str_col, IIF(LEN(str_col) > 0, PATINDEX('%[^0]%', LEFT(str_col, LEN(str_col) - 1) + '.'), 0), LEN(str_col))

'0', '00'등에서도 잘 작동합니다.


0

이 시도:

replace(ltrim(replace(@str, '0', ' ')), ' ', '0')

0

int로 변환하지 않으려면 null을 처리 할 수 ​​있으므로이 논리를 선호합니다. IFNULL (field, LTRIM (field, '0'))


0

MySQL에서는 이것을 할 수 있습니다 ...

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