218

As compared to say:

REPLICATE(@padchar, @len - LEN(@str)) + @str
Cade Roux
  • 88,164
  • 40
  • 182
  • 265
  • I rolled back the last edit. The question gives one way - I was looking for more optimal ways. The edit lost that implication in search of some other quality. – Cade Roux Mar 19 '12 at 01:30

20 Answers20

338

This is simply an inefficient use of SQL, no matter how you do it.

perhaps something like

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

where X is your padding character and @n is the number of characters in the resulting string (assuming you need the padding because you are dealing with a fixed length).

But as I said you should really avoid doing this in your database.

AlexCuse
  • 18,008
  • 5
  • 42
  • 51
  • 3
    There's times where it's needed ... for example, fetching a subset of data for a paged screen. – Beep beep Sep 21 '10 at 05:06
  • 8
    +1 Just tested a load of different methods and this was the fastest. You might need to `RTRIM(@str)` though if that can contain trailing spaces. – Martin Smith Apr 03 '11 at 10:54
  • 1
    +1 Was puzzling over why my char(6) wasn't padding correctly, and the RTRIM on the input variable saved me some head scratching – jkelley Apr 26 '11 at 18:16
  • @MartinSmith Thank you... I've been trying to figure out why my string wasn't working and rtrim fixed it. TY. – WernerCD Jan 16 '12 at 15:39
  • Just used this to update a specific column like this: 'UPDATE table SET column = right('00000000'+ rtrim(column), 8) WHERE predicates;' it updated each of the values in that column to the same string, padded with zeroes, to a total string length of 8. – Jim Wolff Apr 30 '13 at 12:11
  • I had to do: RIGHT('0000000' + ltrim(STR(@intID)), 8) – Brent Nov 20 '13 at 17:45
  • 4
    This is a great answer. Of course you should avoid doing this when you don't have to, but sometimes it's unavoidable; in my case I don't have the choice to do it in C# due to deployment constraints, and someone stored a franchise number as an INT when it should have been a 5 character numeric string with leading zeros. This helped immensely. – Jim Apr 18 '14 at 15:37
  • Good answer. I found this method to be about 10x faster than any method using REPLACE. This will be handy as I have millions of records to process. – Doctuh D. Sep 25 '14 at 19:14
  • please add information about [`lpad`](https://www.w3schools.com/sql/func_mysql_lpad.asp) in yout answer – diralik Dec 23 '17 at 11:08
  • I'm not sure how much sense lpad makes for set-based operations since you need to calculate length to tell how much to pad the value. – AlexCuse Jan 04 '18 at 03:49
  • Just a word of caution: this is not completely equivalent to the technique described in the question. If the `@str` is wider than `@n`, this will cut its left portion, while the the technique described in the question will return NULL. – Branko Dimitrijevic Dec 02 '20 at 16:48
65

I know this was originally asked back in 2008, but there are some new functions that were introduced with SQL Server 2012. The FORMAT function simplifies padding left with zeros nicely. It will also perform the conversion for you:

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

Update:

I wanted to test the actual efficiency of the FORMAT function myself. I was quite surprised to find the efficiency was not very good compared to the original answer from AlexCuse. Although I find the FORMAT function cleaner, it is not very efficient in terms of execution time. The Tally table I used has 64,000 records. Kudos to Martin Smith for pointing out execution time efficiency.

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

SQL Server Execution Times: CPU time = 2157 ms, elapsed time = 2696 ms.

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

SQL Server Execution Times:

CPU time = 31 ms, elapsed time = 235 ms.

Community
  • 1
  • 1
jediCouncilor
  • 798
  • 5
  • 9
  • 1
    This was exactly what I was looking for. Official help for FORMAT: https://msdn.microsoft.com/es-MX/library/hh213505.aspx – Fer García Apr 17 '15 at 20:40
  • 1
    This applies to SQL Server 2014 onwards according to MSDN and my own experience of trying it on SQL Server 2012. – arame3333 Oct 22 '15 at 13:54
  • 6
    This will not be the "most efficient" way as asked though. In this example Format takes 180 seconds vs 12 seconds. http://stackoverflow.com/a/27447244/73226 – Martin Smith Dec 27 '15 at 15:00
  • 3
    It may be "most efficient" in terms of the time taken for the programmer to make use of it! – underscore_d Mar 23 '18 at 14:25
  • 1
    Given that you specify a decimal format, this notably does not work for other types of strings that do not contain only a single decimal. Notably the question did not ask about string decimal. – Kissaki Sep 24 '21 at 07:55
  • This should be the accepted answer now. Simple and clean. – Grimm Apr 25 '23 at 09:07
36

Several people gave versions of this:

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

be careful with that because it will truncate your actual data if it is longer than n.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Kevin
  • 7,162
  • 11
  • 46
  • 70
  • 1
    Please write that as a comment to the answers in question. This post lost refers to others but lost the explicit reference and context. Answers should be self-contained answers. Comments and caveats about answer/solutions should be comments on those. – Kissaki Sep 24 '21 at 07:57
16
@padstr = REPLICATE(@padchar, @len) -- this can be cached, done only once

SELECT RIGHT(@padstr + @str, @len)
Sklivvz
  • 30,601
  • 24
  • 116
  • 172
10

Perhaps an over kill I have these UDFs to pad left and right

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

and to right

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
TonyP
  • 5,655
  • 13
  • 60
  • 94
  • The only problem with scalar UDFs is that they perform far more poorly than equivalent code inline (plus there's data type issues). Here's to hoping they introduce better scalar UDF performance and/or inline scalar UDFs in a future version. – Cade Roux Jun 05 '10 at 22:42
  • If you specify a length less than the length of var, then these functions return null. Wrap each of the replicate statements with an isnull statement to simply return var if the length is less. isnull(replicate(...), '') – Jersey Dude Mar 27 '12 at 22:51
  • Adding WITH SCHEMABINDING to the function declaration will improve the efficiency of the user-defined functions. This is something I recommend adding by default to all your functions unless you have a compelling reason to remove it. – EricI Mar 10 '15 at 05:05
9

I'm not sure that the method that you give is really inefficient, but an alternate way, as long as it doesn't have to be flexible in the length or padding character, would be (assuming that you want to pad it with "0" to 10 characters:

DECLARE
   @pad_characters VARCHAR(10)

SET @pad_characters = '0000000000'

SELECT RIGHT(@pad_characters + @str, 10)
Tom H
  • 46,766
  • 14
  • 87
  • 128
  • 1
    This one was easiest for me to run with - thank you! How I used: https://gist.github.com/b9fcb44f40dad1c10cbc21f73321c148 – Christopher Jun 10 '22 at 12:21
3

I hope this helps someone.

STUFF ( character_expression , start , length ,character_expression )

select stuff(@str, 1, 0, replicate('0', @n - len(@str)))
Suraj Kumar
  • 5,547
  • 8
  • 20
  • 42
vnRock
  • 137
  • 3
2

probably overkill, I often use this 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

So that you can do things like:

select dbo.f_pad_before('aaa', 10, '_')
ila
  • 4,706
  • 7
  • 37
  • 41
2

this is a simple way to pad left:

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

Where x is the pad number and y is the pad character.

sample:

REPLACE(STR(FACT_HEAD.FACT_NO, 3, 0), ' ', 0)
Ahmad
  • 21
  • 1
2

I liked vnRocks solution, here it is in the form of a 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
Kevin
  • 21
  • 1
1
select right(replicate(@padchar, @len) + @str, @len)
Gordon Bell
  • 13,337
  • 3
  • 45
  • 64
1

In SQL Server 2005 and later you could create a CLR function to do this.

Kramii
  • 8,379
  • 4
  • 32
  • 38
1

How about this:

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

3 is the number of zeros to pad

A_Sk
  • 4,532
  • 3
  • 27
  • 51
joshblair
  • 877
  • 1
  • 6
  • 14
0

I use this one. It allows you to determine the length you want the result to be as well as a default padding character if one is not provided. Of course you can customize the length of the input and output for whatever maximums you are running into.

/*===============================================================
 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

Your mileage may vary. :-)

Joey Morgan
Programmer/Analyst Principal I
WellPoint Medicaid Business Unit

Joseph Morgan
  • 163
  • 1
  • 9
0

Here's my solution, which avoids truncated strings and uses plain ol' SQL. Thanks to @AlexCuse, @Kevin and @Sklivvz, whose solutions are the foundation of this code.

 --[@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;
Mass Dot Net
  • 2,150
  • 9
  • 38
  • 50
0

I know this isn't adding much to the conversation at this point but I'm running a file generation procedure and its going incredibly slow. I've been using replicate and saw this trim method and figured I'd give it a shot.

You can see in my code where the switch between the two is in addition to the new @padding variable (and the limitation that now exists). I ran my procedure with the function in both states with the same results in execution time. So at least in SQLServer2016, I'm not seeing any difference in efficiency that other found.

Anyways, here's my UDF that I wrote years ago plus the changes today which is much the same as other's other than it has a LEFT/RIGHT param option and some error checking.

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
blind Skwirl
  • 321
  • 3
  • 6
0

I have one function that lpad with x decimals: CREATE FUNCTION [dbo].[LPAD_DEC] ( -- Add the parameters for the function here @pad nvarchar(MAX), @string nvarchar(MAX), @length int, @dec int ) RETURNS nvarchar(max) AS BEGIN -- Declare the return variable here 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

END

0

Here is my solution. I can pad any character and it is fast. Went with simplicity. You can change variable size to meet your needs.

Updated with a parameter to handle what to return if null: null will return a null if null

CREATE OR ALTER FUNCTION code.fnConvert_PadLeft(
    @in_str nvarchar(1024),
    @pad_length int, 
    @pad_char nchar(1) = ' ', 
    @rtn_null NVARCHAR(1024) = '')
RETURNS NVARCHAR(1024)
AS
BEGIN
     DECLARE @rtn  NCHAR(1024) = ' '
     RETURN RIGHT(REPLACE(@rtn,' ',@pad_char)+ISNULL(@in_str,@rtn_null), @pad_length)
END
GO

CREATE OR ALTER FUNCTION code.fnConvert_PadRight(
    @in_str nvarchar(1024), 
    @pad_length int, 
    @pad_char nchar(1) = ' ', 
    @rtn_null NVARCHAR(1024) = '')
RETURNS NVARCHAR(1024)
AS
BEGIN
     DECLARE @rtn  NCHAR(1024) = ' '
     RETURN LEFT(ISNULL(@in_str,@rtn_null)+REPLACE(@rtn,' ',@pad_char), @pad_length)
END
GO 

-- Example
SET STATISTICS time ON 
SELECT code.fnConvert_PadLeft('88',10,'0',''), 
    code.fnConvert_PadLeft(null,10,'0',''), 
    code.fnConvert_PadLeft(null,10,'0',null), 
    code.fnConvert_PadRight('88',10,'0',''), 
    code.fnConvert_PadRight(null,10,'0',''),
    code.fnConvert_PadRight(null,10,'0',NULL)


0000000088  0000000000  NULL    8800000000  0000000000  NULL

DGM0522
  • 1
  • 1
-1

To provide numerical values rounded to two decimal places but right-padded with zeros if required I have:

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)

If anyone can think of a neater way, that would be appreciated - the above seems clumsy.

Note: in this instance, I'm using SQL Server to email reports in HTML format and so wish to format the information without involving an additional tool to parse the data.

Neysor
  • 3,893
  • 11
  • 34
  • 66
mattpm
  • 1,310
  • 2
  • 19
  • 26
  • 1
    Didn't know SQL Server allowed you to declare a variable without specifying its type. Anyway, your method does seem "clumsy" for a non-working one. :) – Andriy M Mar 19 '12 at 01:13
-4

Here is how I would normally pad a varchar

WHILE Len(@String) < 8
BEGIN
    SELECT @String = '0' + @String
END
Deanos
  • 1
  • 14
    Wow, this is amazingly bad. – Hogan Mar 19 '12 at 17:56
  • 1
    Loops, cursors, etc are all generally bad in SQL. Might be fine in application code but not in SQL. Some exceptions but this is not one of them. – Davos Nov 22 '12 at 23:32