SQL Serverで先行ゼロをトリミングするためのより良いテクニックは?


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 2009年

これは一般的な手法です。これらは通常、未準拠のフィールドに入力されるアカウント番号であり、データウェアハウスがETLで使用する確認ルールに一致することを確認する必要があります(もちろん、より完全な機能を備えたSSIS環境では、これらを使用すると想定しています)。 TrimStart)。
Cade Roux、

回答:


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

2
賢い、私がそれを考えていたらいいのに。
Cade Roux、

4
気にしないで、「。」それはパターンを見つけるためにのみ使用されるため、部分文字列にはありません-思ったよりもずっと賢いです。
Cade Roux、

2
これを関数にカプセル化すると、クエリが遅くなります。理由はよくわかりませんが、型変換に関係していると思います。SUBSTRINGインラインの使用ははるかに高速でした。
ロニーオーバーバイ2013

1
質問では、これの問題はゼロ( '0')を解析すると空白になることを示しています。「0」の値と空白の値の違いを認識できる必要があります。完全な解決策については私の投稿を参照してください:stackoverflow.com/a/21805081/555798
MikeTeeVee

1
@Arvo Wow ...しばらくの間、私は混乱し、私を助けようとしていたこの質問に答えたと思いました。ArvoSOで初めて見たのは初めてです。
Arvo Bowen

41

値をキャストしてみませんか INTEGERから、戻りVARCHARませんか?

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

--------
       0

11
これは文字列列なので、時々非数値データを期待していると思います。データがほとんど数値のみであるMRN番号のようなもの。
Joel Coehoorn、2009年

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 2013

を使用してもBIGINT、一部のタイプの文字列はこの変換に失敗します。0001E123たとえば考えてみてください。
roaima

1
私のテスト(および経験)から、これは受け入れられた回答と比較して比較的コストのかかる操作です。パフォーマンス上の理由から、データ型を変更したり、さまざまな型のデータを比較したりすることは、可能であれば可能です。
reedstonefood 2015年

14

すべてがゼロ(または単一のゼロ)の場合に考慮しないためのその他の回答。
空の文字列を常にデフォルトでゼロに設定するものもありますが、これは空白のままにすることになっている場合は誤りです。
元の質問をもう一度読んでください。これは、質問者が望むものに答えます。

ソリューション#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

概要:

私が上記のものを使用して、先行ゼロの1回限りの削除を行うことができます。
何度も再利用する予定がある場合は、インラインテーブル値関数(ITVF)に配置します。
UDFのパフォーマンスの問題に関する懸念は理解できます。
ただし、この問題はAll-Scalar-FunctionsおよびMulti-Statement-Table-Functionsにのみ適用されます。
ITVFを使用しても問題ありません。

サードパーティのデータベースにも同じ問題があります。
英数字フィールドでは、先頭のスペースなしで多くのフィールドが入力されます。
これにより、欠落している先行ゼロをクリーンアップしないと、結合が不可能になります。

結論:

先行ゼロを削除する代わりに、結合を行うときにトリム値に先行ゼロを埋め込むことを検討する必要がある場合があります。
さらに、先行ゼロを追加してテーブルのデータをクリーンアップし、インデックスを再構築します。
私はこれがずっと速くて複雑ではないと思います。

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

4
@DiegoQueiroz答えが間違っている場合は、ランクを下げて、機能しない理由を説明してください。答えはうまくいくが、あなたにとって包括的すぎる場合は、私またはこのサイトの他のメンバーをダウンランクしないでください。コメントありがとうございます。聞くのは良いフィードバックです-私はこれを心から言います。
MikeTeeVee 2017年

5

スペースの代わりに、0を、通常は列のテキストに含まれてはならない「まれな」空白文字に置き換えます。このような列には、おそらくラインフィードで十分です。その後、通常どおりLTrimを実行し、特殊文字を再び0に置き換えることができます。


3

文字列が完全にゼロで構成されている場合、次のコードは「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

これは、値にゼロがない(空白である)場合にもゼロを返します。
MikeTeeVee 2014

str_col + 'がある理由 そしてstr_colだけでなく?ドットは何をしますか?
Muflix、2015

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

これは、値にゼロがない(空白である)場合にもゼロを返します。上記の質問でUDFの使用を避けるように具体的に述べている場合、この回答でもmulti-statement-scalar-functionを使用します。
MikeTeeVee 2014

2

文字列が数値の場合、cast(value as int)は常に機能します


これは質問に対する答えを提供しません。批評したり、著者に説明を要求するには、投稿の下にコメントを残してください。- レビューから
Josip Ivic

1
実際にはそれが機能するので答えです?回答を長くする必要はありません
ティクラ

回答は長くする必要はありませんが、可能であれば完全なものにする必要があり、回答はそうではありません。結果のデータ型を変更します。SELECT CAST(CAST(value AS Int)AS VARCHAR):これはより良い応答だったと思います。また、計算された値が2.1x10 ^ 9(8桁の制限)を超えると、Intでエラーが発生することにも言及する必要があります。BigIntを使用すると、値が約19桁(9.2x10 ^ 18)を超えるとエラーが発生します。
J.クリスコンプトン

2

これの私のバージョンは、他の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の提案は私たちのニーズに応えてくれました。

私たちの場合のフィールドはすでに文字列であり、先頭のゼロだけをトリミングする必要がありました。ほとんどの場合それは数値ですが、時々文字があるため、以前のINT変換はクラッシュしました。


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

これは、INTに変換できる文字列の長さに制限があります


これがうまくいくと思う理由について、回答でもう少し説明していただけますか?これが先行ゼロがたくさんあるゼロ以外の数値である場合はどうなりますか?
テゴスト

数値が18桁以下の場合(および制限が実際には9.2x10 ^ 18であるため、ほとんどの19桁の数値は機能します)、SELECT CAST(CAST(@Field_Name AS BigInt)AS VARCHAR)を使用して先行ゼロを取り除くことができます。注:これは、数値以外の文字(ダッシュ、文字、ピリオドなど)がある場合、エラーメッセージ8114「データ型varcharからbigintへの変換中にエラーが発生しました」で失敗します。
J.クリスコンプトン

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

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.