Cách T-SQL hiệu quả nhất để đệm một varchar bên trái đến một độ dài nhất định?


205

So với nói:

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

Tôi quay lại chỉnh sửa cuối cùng. Câu hỏi đưa ra một cách - tôi đang tìm kiếm những cách tối ưu hơn. Bản chỉnh sửa bị mất hàm ý trong việc tìm kiếm một số chất lượng khác.
Cade Roux

Câu trả lời:


323

Đây đơn giản là việc sử dụng SQL không hiệu quả, bất kể bạn làm như thế nào.

có lẽ một cái gì đó như

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

Trong đó X là ký tự đệm của bạn và @n là số ký tự trong chuỗi kết quả (giả sử bạn cần đệm vì bạn đang xử lý một độ dài cố định).

Nhưng như tôi đã nói, bạn thực sự nên tránh làm điều này trong cơ sở dữ liệu của bạn.


2
Có những lúc cần thiết ... ví dụ, tìm nạp một tập hợp con dữ liệu cho màn hình phân trang.
bíp bíp

7
+1 Chỉ cần thử nghiệm tải các phương pháp khác nhau và đây là cách nhanh nhất. Bạn có thể cần phải RTRIM(@str)mặc dù nếu điều đó có thể chứa dấu cách.
Martin Smith

1
+1 Đã khó hiểu tại sao char của tôi (6) không đệm chính xác và RTRIM trên biến đầu vào đã giúp tôi tiết kiệm được một số vết xước
jkelley

@MartinSmith Cảm ơn bạn ... Tôi đã cố gắng tìm hiểu tại sao chuỗi của tôi không hoạt động và rtrim đã sửa nó. TY.
WernerCD

3
Đây là một câu trả lời tuyệt vời. Tất nhiên bạn nên tránh làm điều này khi bạn không cần, nhưng đôi khi điều đó là không thể tránh khỏi; trong trường hợp của tôi, tôi không có lựa chọn làm điều đó trong C # do các ràng buộc triển khai và ai đó đã lưu trữ một số nhượng quyền dưới dạng INT khi nó phải là một chuỗi số gồm 5 ký tự có các số 0 đứng đầu. Điều này đã giúp rất nhiều.
Jim

57

Tôi biết điều này ban đầu được hỏi lại vào năm 2008, nhưng có một số chức năng mới đã được giới thiệu với SQL Server 2012. Hàm FORMAT đơn giản hóa phần đệm còn lại với số không độc đáo. Nó cũng sẽ thực hiện chuyển đổi cho bạn:

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

Cập nhật:

Tôi muốn tự mình kiểm tra hiệu quả thực tế của hàm FORMAT. Tôi khá ngạc nhiên khi thấy hiệu quả không tốt lắm so với câu trả lời ban đầu từ AlexCuse . Mặc dù tôi thấy trình dọn dẹp hàm FORMAT không hiệu quả về mặt thời gian thực hiện. Bảng Tally tôi sử dụng có 64.000 hồ sơ. Kudos cho Martin Smith để chỉ ra hiệu quả thời gian thực hiện.

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

Thời gian thực hiện máy chủ SQL: Thời gian CPU = 2157 ms, thời gian trôi qua = 2696 ms.

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

Thời gian thực thi máy chủ SQL:

Thời gian CPU = 31 ms, thời gian trôi qua = 235 ms.


1
Đây chính xác là những gì tôi đang tìm kiếm. Trợ giúp chính thức cho FORMAT: msdn.microsoft.com/es-MX/l Library / hh213505.aspx
Fer García

1
Điều này áp dụng cho SQL Server 2014 trở đi theo MSDN và kinh nghiệm của riêng tôi về việc thử nó trên SQL Server 2012.
arame3333

5
Đây sẽ không phải là cách "hiệu quả nhất" như đã hỏi. Trong ví dụ này, Format mất 180 giây so với 12 giây. stackoverflow.com/a/27447244/73226
Martin Smith

2
Nó có thể là "hiệu quả nhất" về thời gian dành cho lập trình viên để sử dụng nó!
gạch dưới

36

Một số người đã đưa ra các phiên bản này:

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

hãy cẩn thận với điều đó bởi vì nó sẽ cắt bớt dữ liệu thực tế của bạn nếu nó dài hơn n.


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

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

9

Có lẽ giết chết tôi có các UDF này để chuyển sang trái và phải

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

và sang phải

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

Vấn đề duy nhất với UDF vô hướng là chúng hoạt động kém hơn nhiều so với mã nội tuyến tương đương (cộng với các vấn đề về kiểu dữ liệu). Đây là hy vọng họ giới thiệu hiệu suất UDF vô hướng tốt hơn và / hoặc UDF vô hướng nội tuyến trong phiên bản tương lai.
Cade Roux

Nếu bạn chỉ định độ dài nhỏ hơn độ dài của var, thì các hàm này trả về null. Bao bọc từng câu lệnh sao chép bằng một câu lệnh isnull để trả về var nếu độ dài nhỏ hơn. isnull (sao chép (...), '')
Jersey Dude

Thêm VỚI SCHEMABINDING vào khai báo hàm sẽ cải thiện hiệu quả của các hàm do người dùng định nghĩa. Đây là điều tôi khuyên bạn nên thêm theo mặc định cho tất cả các chức năng của mình trừ khi bạn có lý do thuyết phục để xóa nó.
EricI 10/03/2015

7

Tôi không chắc chắn rằng phương pháp mà bạn đưa ra thực sự không hiệu quả, nhưng một cách khác, miễn là nó không phải linh hoạt về độ dài hoặc ký tự đệm, sẽ là (giả sử rằng bạn muốn đệm nó với " 0 "đến 10 ký tự:

DECLARE
   @pad_characters VARCHAR(10)

SET @pad_characters = '0000000000'

SELECT RIGHT(@pad_characters + @str, 10)

2

có lẽ quá mức cần thiết, tôi thường sử dụng UDF này:

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

Để bạn có thể làm những việc như:

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

Điều này thực sự được sử dụng trong một udf phải thực hiện một số điều khác để tuân thủ một số dữ liệu.
Cade Roux

Điều này cũng hoạt động hoàn hảo nếu bạn kết hợp các loại char và varchar.
Jimmy Baker

2

Tôi thích giải pháp vnRocks, ở đây nó ở dạng 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

Đây là một cách đơn giản để đệm trái:

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

Trong trường hợp xlà số pad và ylà nhân vật pad.

mẫu vật:

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

Bạn dường như đang nói về số đệm bên trái như vậy 1trở thành 001.
Martin Smith


1

Trong SQL Server 2005 trở lên, bạn có thể tạo hàm CLR để thực hiện việc này.


1

Còn cái này thì sao:

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

3 là số lượng zerosđể đệm


Điều này đã giúp tôi: CONCAT (REPLACE (SPACE (@n - LENGTH (@str)), '', '0'), @str)
ingham

1

Tôi hi vọng điêu nay se giup được ai đo.

STUFF ( character_expression , start , length ,character_expression )

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

0

Tôi sử dụng cái này Nó cho phép bạn xác định độ dài bạn muốn kết quả cũng như ký tự đệm mặc định nếu không được cung cấp. Tất nhiên bạn có thể tùy chỉnh độ dài của đầu vào và đầu ra cho bất kỳ mức tối đa nào bạn đang chạy vào.

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

Số dặm của bạn có thể thay đổi. :-)

Joey Morgan
Lập trình viên / Chuyên viên phân tích Hiệu trưởng I
Đơn vị kinh doanh Trợ cấp y tế


0

Đây là giải pháp của tôi, giúp tránh các chuỗi bị cắt cụt và sử dụng SQL đơn giản. Cảm ơn @AlexCuse , @Kevin @Sklivvz , mà giải pháp là nền tảng của mã này.

 --[@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

Tôi biết điều này không thêm nhiều vào cuộc trò chuyện vào thời điểm này nhưng tôi đang chạy một quy trình tạo tệp và nó sẽ diễn ra rất chậm. Tôi đã sử dụng bản sao và thấy phương pháp cắt này và hình dung tôi sẽ cho nó một shot.

Bạn có thể thấy trong mã của tôi, nơi chuyển đổi giữa hai bên ngoài biến @padding mới (và giới hạn hiện tồn tại). Tôi đã chạy thủ tục của mình với hàm ở cả hai trạng thái có cùng kết quả trong thời gian thực hiện. Vì vậy, ít nhất là trong SQLServer2016, tôi không thấy bất kỳ sự khác biệt nào về hiệu quả mà người khác tìm thấy.

Dù sao, đây là UDF của tôi mà tôi đã viết cách đây nhiều năm cộng với những thay đổi ngày nay giống với những thay đổi khác không phải là tùy chọn LEFT / RIGHT và kiểm tra lỗi.

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

Tôi có một chức năng mà lpad có x số thập phân: CREATE FUNCTION [dbo]. [LPAD_DEC] (- Thêm các tham số cho chức năng ở đây @pad nvarchar (MAX), @opes nvarchar (MAX), @length int, @dec int ) TRẢ LẠI nvarchar (tối đa) NHƯ BEGIN - Khai báo biến trả về ở đây 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

KẾT THÚC


-1

Để cung cấp các giá trị số được làm tròn đến hai chữ số thập phân nhưng được đệm bằng số không nếu cần tôi có:

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)

Nếu bất cứ ai có thể nghĩ ra một cách gọn gàng hơn, điều đó sẽ được đánh giá cao - những điều trên có vẻ vụng về .

Lưu ý : trong trường hợp này, tôi đang sử dụng SQL Server để gửi email báo cáo ở định dạng HTML và vì vậy muốn định dạng thông tin mà không cần một công cụ bổ sung để phân tích dữ liệu.


1
Không biết SQL Server cho phép bạn khai báo một biến mà không chỉ định loại của nó. Dù sao, phương pháp của bạn có vẻ "vụng về" đối với một người không làm việc. :)
Andriy M

-4

Đây là cách tôi thường đệm một varchar

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

14
Wow, điều này thật đáng kinh ngạc.
Hogan

1
Vòng lặp, con trỏ, v.v ... nói chung đều tệ trong SQL. Có thể tốt trong mã ứng dụng nhưng không phải trong SQL. Một số trường hợp ngoại lệ nhưng đây không phải là một trong số họ.
Davos
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.