左側のvarcharを特定の長さに埋め込む最も効率的なT-SQLの方法は?


205

言うと比較して:

REPLICATE(@padchar, @len - LEN(@str)) + @str

最後の編集をロールバックしました。質問は1つの方法を与えます-私はより最適な方法を探していました。編集は他のいくつかの品質を求めてその意味合いを失いました。
Cade Roux、2012年

回答:


323

これは、SQLの使用方法に関係なく、単に非効率的なSQLの使用です。

おそらく何かのような

right('XXXXXXXXXXXX'+ rtrim(@str), @n)

ここで、Xはパディング文字であり、@ nは結果の文字列の文字数です(固定長を扱っているためパディングが必要であると想定しています)。

しかし、私が言ったように、あなたは本当にあなたのデータベースでこれをすることを避けるべきです。


2
必要な場合があります。たとえば、ページ付き画面のデータのサブセットをフェッチします。
ビープ音

7
+1さまざまなメソッドのロードをテストしたところ、これが最速でした。RTRIM(@str)末尾にスペースを含めることができる場合は、そうする必要があるかもしれません。
マーティン・スミス

1
+1なぜchar(6)が正しくパディングされなかったのかと戸惑い、入力変数のRTRIMで
頭を悩ませる手間を省いた

@MartinSmithありがとう...私の文字列が機能しなかった理由を解明しようとしていて、rtrimが修正しました。TY。
WernerCD、2012年

3
これは素晴らしい答えです。もちろん、そうする必要がない場合はこれを避けるべきですが、避けられない場合もあります。私の場合、展開の制約のためにC#で実行することはできません。また、フランチャイズ番号が先頭にゼロが付いた5文字の数値文字列であるはずのINTとして格納されていました。これは非常に役立ちました。
ジム

57

これは元々は2008年に尋ねられたことを知っていますが、SQL Server 2012で導入されたいくつかの新しい関数があります。FORMAT関数は、ゼロでの左側の埋め込みを簡単に簡素化します。また、変換も実行します。

declare @n as int = 2
select FORMAT(@n, 'd10') as padWithZeros

更新:

FORMAT関数の実際の効率を自分でテストしたかったのです。AlexCuseからの元の回答と比較して効率があまり良くないことを知って、私は非常に驚きました。FORMAT関数はきれいに見えますが、実行時間の点では効率的ではありません。私が使用したTallyテーブルには64,000レコードがあります。実行時間の効率性を指摘してくれたMartin Smithに称賛。

SET STATISTICS TIME ON
select FORMAT(N, 'd10') as padWithZeros from Tally
SET STATISTICS TIME OFF

SQL Server実行時間:CPU時間= 2157ミリ秒、経過時間= 2696ミリ秒。

SET STATISTICS TIME ON
select right('0000000000'+ rtrim(cast(N as varchar(5))), 10) from Tally
SET STATISTICS TIME OFF

SQL Server実行時間:

CPU時間= 31ミリ秒、経過時間= 235ミリ秒。


1
これはまさに私が探していたものでした。FORMATの公式ヘルプ:msdn.microsoft.com/es-MX/library/hh213505.aspx
フェル・ガルシア

1
これ以降、MSDNおよびSQL Server 2012上でそれをしようとする私自身の経験に応じてSQL Serverの2014年に適用されます
arame3333

5
これは、質問された「最も効率的な」方法ではありません。この例では、フォーマットには180秒と12秒かかります。stackoverflow.com/a/27447244/73226
Martin Smith

2
プログラマーがそれを利用するのにかかる時間に関しては、「最も効率的」かもしれません。
underscore_d

36

数人がこれのバージョンを与えました:

right('XXXXXXXXXXXX'+ @str, @n)

実際のデータがnより長い場合は切り捨てられるため、注意してください。


17
@padstr = REPLICATE(@padchar, @len) -- this can be cached, done only once

SELECT RIGHT(@padstr + @str, @len)

9

おそらく、私はこれらのUDFを左と右に埋め込むために過剰な殺害

ALTER   Function [dbo].[fsPadLeft](@var varchar(200),@padChar char(1)='0',@len int)
returns varchar(300)
as
Begin

return replicate(@PadChar,@len-Len(@var))+@var

end

そして右へ

ALTER function [dbo].[fsPadRight](@var varchar(200),@padchar char(1)='0', @len int) returns varchar(201) as
Begin

--select @padChar=' ',@len=200,@var='hello'


return  @var+replicate(@PadChar,@len-Len(@var))
end

スカラーUDFの唯一の問題は、同等のコードよりもパフォーマンスがはるかに悪いことです(さらに、データ型の問題があります)。ここでは、将来のバージョンでより優れたスカラーUDFパフォーマンスやインラインスカラーUDFが導入されることを期待しています。
Cade Roux

varの長さより短い長さを指定すると、これらの関数はnullを返します。それぞれのレプリケートステートメントをisnullステートメントでラップして、長さが短い場合は単にvarを返します。isnull(replicate(...)、 '')
Jersey Dude

WITH SCHEMABINDINGを関数宣言に追加すると、ユーザー定義関数の効率が向上します。これは、やむを得ない理由で削除しない限り、デフォルトですべての関数に追加することをお勧めします。
EricI 2015年

7

あなたが与える方法が本当に非効率的であるかどうかはわかりませんが、長さやパディング文字が柔軟である必要がない限り、別の方法があります(「 0から10文字:

DECLARE
   @pad_characters VARCHAR(10)

SET @pad_characters = '0000000000'

SELECT RIGHT(@pad_characters + @str, 10)

2

おそらくやりすぎです。私はこのUDFをよく使用します。

CREATE FUNCTION [dbo].[f_pad_before](@string VARCHAR(255), @desired_length INTEGER, @pad_character CHAR(1))
RETURNS VARCHAR(255) AS  
BEGIN

-- Prefix the required number of spaces to bulk up the string and then replace the spaces with the desired character
 RETURN ltrim(rtrim(
        CASE
          WHEN LEN(@string) < @desired_length
            THEN REPLACE(SPACE(@desired_length - LEN(@string)), ' ', @pad_character) + @string
          ELSE @string
        END
        ))
END

次のようなことができるように:

select dbo.f_pad_before('aaa', 10, '_')

これは実際には、一部のデータを適合させるために他のいくつかのことを行う必要があるudfで使用されます。
Cade Roux、

これは、char型とvarchar型を組み合わせる場合にも完全に機能します。
ジミーベイカー

2

私はvnRocksソリューションが好きでした、ここではそれはudfの形式です

create function PadLeft(
      @String varchar(8000)
     ,@NumChars int
     ,@PadChar char(1) = ' ')
returns varchar(8000)
as
begin
    return stuff(@String, 1, 0, replicate(@PadChar, @NumChars - len(@String)))
end

2

これは左にパディングする簡単な方法です:

REPLACE(STR(FACT_HEAD.FACT_NO, x, 0), ' ', y)

どこにxあるパッド番号とyパッド文字です。

サンプル:

REPLACE(STR(FACT_HEAD.FACT_NO, 3, 0), ' ', 0)

あなたは、このような左パディング番号について話しているよう1になり001
Martin Smith、



1

これはどう:

replace((space(3 - len(MyField))

3はzerosパッドする数です


これは私を助けました:CONCAT(REPLACE(SPACE(@n-LENGTH(@str))、 ''、 '0')、@str)
ingham

1

これが誰かの役に立つことを願っています。

STUFF ( character_expression , start , length ,character_expression )

select stuff(@str, 1, 0, replicate('0', @n - len(@str)))

0

私はこれを使います。これにより、結果の長さを指定したり、デフォルトの埋め込み文字が提供されていない場合はデフォルトの埋め込み文字を決定したりできます。もちろん、実行している最大値に合わせて、入力と出力の長さをカスタマイズできます。

/*===============================================================
 Author         : Joey Morgan
 Create date    : November 1, 2012
 Description    : Pads the string @MyStr with the character in 
                : @PadChar so all results have the same length
 ================================================================*/
 CREATE FUNCTION [dbo].[svfn_AMS_PAD_STRING]
        (
         @MyStr VARCHAR(25),
         @LENGTH INT,
         @PadChar CHAR(1) = NULL
        )
RETURNS VARCHAR(25)
 AS 
      BEGIN
        SET @PadChar = ISNULL(@PadChar, '0');
        DECLARE @Result VARCHAR(25);
        SELECT
            @Result = RIGHT(SUBSTRING(REPLICATE('0', @LENGTH), 1,
                                      (@LENGTH + 1) - LEN(RTRIM(@MyStr)))
                            + RTRIM(@MyStr), @LENGTH)

        RETURN @Result

      END

あなたのマイレージは異なる場合があります。:-)

Joey Morgan
プログラマー/アナリストプリンシパルI
WellPointメディケイドビジネスユニット


0

これが私の解決策です。これは、文字列の切り捨てを回避し、プレーンなSQLを使用します。@AlexCuse@Kevin、および@Sklivvzに感謝します。これらのソリューションはこのコードの基礎となっています。

 --[@charToPadStringWith] is the character you want to pad the string with.
declare @charToPadStringWith char(1) = 'X';

-- Generate a table of values to test with.
declare @stringValues table (RowId int IDENTITY(1,1) NOT NULL PRIMARY KEY, StringValue varchar(max) NULL);
insert into @stringValues (StringValue) values (null), (''), ('_'), ('A'), ('ABCDE'), ('1234567890');

-- Generate a table to store testing results in.
declare @testingResults table (RowId int IDENTITY(1,1) NOT NULL PRIMARY KEY, StringValue varchar(max) NULL, PaddedStringValue varchar(max) NULL);

-- Get the length of the longest string, then pad all strings based on that length.
declare @maxLengthOfPaddedString int = (select MAX(LEN(StringValue)) from @stringValues);
declare @longestStringValue varchar(max) = (select top(1) StringValue from @stringValues where LEN(StringValue) = @maxLengthOfPaddedString);
select [@longestStringValue]=@longestStringValue, [@maxLengthOfPaddedString]=@maxLengthOfPaddedString;

-- Loop through each of the test string values, apply padding to it, and store the results in [@testingResults].
while (1=1)
begin
    declare
        @stringValueRowId int,
        @stringValue varchar(max);

    -- Get the next row in the [@stringLengths] table.
    select top(1) @stringValueRowId = RowId, @stringValue = StringValue
    from @stringValues 
    where RowId > isnull(@stringValueRowId, 0) 
    order by RowId;

    if (@@ROWCOUNT = 0) 
        break;

    -- Here is where the padding magic happens.
    declare @paddedStringValue varchar(max) = RIGHT(REPLICATE(@charToPadStringWith, @maxLengthOfPaddedString) + @stringValue, @maxLengthOfPaddedString);

    -- Added to the list of results.
    insert into @testingResults (StringValue, PaddedStringValue) values (@stringValue, @paddedStringValue);
end

-- Get all of the testing results.
select * from @testingResults;

0

これで現時点で会話に多くの影響が及ばないことはわかっていますが、ファイル生成手順を実行していて、信じられないほど遅くなっています。私はレプリケートを使用していて、このトリム方法を見て、試してみると思いました。

2つのコード間の切り替えが新しい@padding変数(および現在存在する制限)に追加されているコードを見ることができます。両方の状態で関数を使用してプロシージャを実行しましたが、実行時間は同じです。したがって、少なくともSQLServer2016では、他の人が見つけたような効率の違いは見られません。

とにかく、ここに私が数年前に書いた私のUDFと、今日の変更点があります。これは、LEFT / RIGHTパラメータオプションといくつかのエラーチェックがあることを除いて、他のものとほとんど同じです。

CREATE FUNCTION PadStringTrim 
(
    @inputStr varchar(500), 
    @finalLength int, 
    @padChar varchar (1),
    @padSide varchar(1)
)
RETURNS VARCHAR(500)

AS BEGIN
    -- the point of this function is to avoid using replicate which is extremely slow in SQL Server
    -- to get away from this though we now have a limitation of how much padding we can add, so I've settled on a hundred character pad 
    DECLARE @padding VARCHAR (100) = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
    SET @padding = REPLACE(@padding, 'X', @padChar)


    SET @inputStr = RTRIM(LTRIM(@inputStr))

    IF LEN(@inputStr) > @finalLength 
        RETURN '!ERROR!' -- can search for ! in the returned text 

    ELSE IF(@finalLength > LEN(@inputStr))
        IF @padSide = 'L'
            SET @inputStr = RIGHT(@padding + @inputStr, @finalLength)
            --SET @inputStr = REPLICATE(@padChar, @finalLength - LEN(@inputStr)) + @inputStr
        ELSE IF @padSide = 'R'
            SET @inputStr = LEFT(@inputStr + @padding, @finalLength)
            --SET @inputStr = @inputStr + REPLICATE(@padChar, @finalLength - LEN(@inputStr)) 



    -- if LEN(@inputStr) = @finalLength we just return it 
    RETURN @inputStr;
END

-- SELECT  dbo.PadStringTrim( tblAccounts.account, 20, '~' , 'R' ) from tblAccounts
-- SELECT  dbo.PadStringTrim( tblAccounts.account, 20, '~' , 'L' ) from tblAccounts

0

10進数xでlpadする関数が1つあります:CREATE FUNCTION [dbo]。[LPAD_DEC](-ここに関数のパラメーターを追加します@pad nvarchar(MAX)、@ string nvarchar(MAX)、@ length int、@ dec int )RETURNS nvarchar(max)AS BEGIN-ここで戻り変数を宣言しますDECLARE @resp nvarchar(max)

IF LEN(@string)=@length
BEGIN
    IF CHARINDEX('.',@string)>0
    BEGIN
        SELECT @resp = CASE SIGN(@string)
            WHEN -1 THEN
                -- Nros negativos grandes con decimales
                concat('-',SUBSTRING(replicate(@pad,@length),1,@length-len(@string)),ltrim(str(abs(@string),@length,@dec)))
            ELSE
                -- Nros positivos grandes con decimales
                concat(SUBSTRING(replicate(@pad,@length),1,@length-len(@string)),ltrim(str(@string,@length,@dec)))                  
            END
    END
    ELSE
    BEGIN
        SELECT @resp = CASE SIGN(@string)
            WHEN -1 THEN
                --Nros negativo grande sin decimales
                concat('-',SUBSTRING(replicate(@pad,@length),1,(@length-3)-len(@string)),ltrim(str(abs(@string),@length,@dec)))
            ELSE
                -- Nros positivos grandes con decimales
                concat(SUBSTRING(replicate(@pad,@length),1,@length-len(@string)),ltrim(str(@string,@length,@dec)))                  
            END                     
    END
END
ELSE
    IF CHARINDEX('.',@string)>0
    BEGIN
        SELECT @resp =CASE SIGN(@string)
            WHEN -1 THEN
                -- Nros negativos con decimales
                concat('-',SUBSTRING(replicate(@pad,@length),1,@length-len(@string)),ltrim(str(abs(@string),@length,@dec)))
            ELSE
                --Ntos positivos con decimales
                concat(SUBSTRING(replicate(@pad,@length),1,@length-len(@string)),ltrim(str(abs(@string),@length,@dec))) 
            END
    END
    ELSE
    BEGIN
        SELECT @resp = CASE SIGN(@string)
            WHEN -1 THEN
                -- Nros Negativos sin decimales
                concat('-',SUBSTRING(replicate(@pad,@length-3),1,(@length-3)-len(@string)),ltrim(str(abs(@string),@length,@dec)))
            ELSE
                -- Nros Positivos sin decimales
                concat(SUBSTRING(replicate(@pad,@length),1,(@length-3)-len(@string)),ltrim(str(abs(@string),@length,@dec)))
            END
    END
RETURN @resp

終わり


-1

小数点以下2桁に四捨五入された数値を提供するが、必要に応じて右にゼロを埋め込むには、次のようにします。

DECLARE @value = 20.1
SET @value = ROUND(@value,2) * 100
PRINT LEFT(CAST(@value AS VARCHAR(20)), LEN(@value)-2) + '.' + RIGHT(CAST(@value AS VARCHAR(20)),2)

誰かがきちんとした方法を考えることができれば、それは高く評価されます-上記のようです 不器用にます。

:この例では、SQL Serverを使用してレポートをHTML形式で電子メールで送信しているため、データを解析する追加のツールを使用せずに情報をフォーマットします。


1
SQL Serverで型を指定せずに変数を宣言できることを知りませんでした。とにかく、あなたの方法は、機能していないものに対しては「不器用」に見えます。:)
Andriy M

-4

これは私が通常varcharを埋める方法です

WHILE Len(@String) < 8
BEGIN
    SELECT @String = '0' + @String
END

14
うわー、これは驚くほど悪いです。
Hogan

1
ループ、カーソルなどはすべて、SQLでは一般的に問題があります。アプリケーションコードでは問題ないかもしれませんが、SQLでは問題ではありません。いくつかの例外がありますが、これはそれらの1つではありません。
ダボス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.