Solda bir varchar belirli bir uzunlukta pad için en verimli T-SQL yolu?


205

Söylediklerine göre:

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

Son düzenlemeyi geri aldım. Soru bir yol veriyor - daha uygun yollar arıyordum. Düzenleme, başka bir kalite arayışındaki bu anlamı kaybetti.
Cade Roux

Yanıtlar:


323

Bu, nasıl yaparsanız yapın, SQL'in verimsiz bir kullanımıdır.

belki böyle bir şey

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

burada X, doldurma karakteriniz ve @n, elde edilen dizgideki karakter sayısıdır (sabit bir uzunlukla uğraştığınız için dolguya ihtiyacınız olduğu varsayılarak).

Ama dediğim gibi bunu gerçekten veritabanınızda yapmaktan kaçınmalısınız.


2
İhtiyaç duyulan zamanlar vardır ... örneğin, disk belleği olan bir ekran için bir veri alt kümesi getiriliyor.
Bip bip

7
+1 Sadece farklı yöntemlerin yükünü test ettim ve bu en hızlısıydı. RTRIM(@str)Yine de bu sondaki boşluklar içeriyorsa, yapmanız gerekebilir .
Martin Smith


@MartinSmith Teşekkür ederim ... Dizimin neden çalışmadığını anlamaya çalışıyorum ve rtrim bunu düzeltti. TY.
WernerCD

3
Bu harika bir cevap. Elbette bunu yapmak zorunda olmadığınızda yapmaktan kaçınmalısınız, ancak bazen kaçınılmazdır; benim durumumda dağıtım kısıtlamaları nedeniyle C # yapmak için seçimim yok ve birisi önde gelen sıfırlar ile 5 karakterlik bir sayısal dize olması gerektiğinde bir INT olarak bir franchise numarası depoladı. Bu çok yardımcı oldu.
Jim

57

Bunun 2008'de geri istendiğini biliyorum, ancak SQL Server 2012 ile tanıtılan bazı yeni işlevler var. FORMAT işlevi sıfırlarla güzelce bırakılan dolguları basitleştirir. Ayrıca sizin için dönüşüm gerçekleştirir:

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

Güncelleme:

FORMAT işlevinin gerçek verimliliğini kendim test etmek istedim. AlexCuse'un orijinal cevabına göre verimliliğin çok iyi olmadığını bulmak oldukça şaşırdım . FORMAT işlevini daha temiz bulmama rağmen, yürütme süresi açısından çok verimli değil. Kullandığım Tally tablosunda 64.000 kayıt var. Yürütme zamanı verimliliğine dikkat çektiği için Martin Smith'e şeref .

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

SQL Server Yürütme Süreleri: CPU süresi = 2157 ms, geçen süre = 2696 ms.

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

SQL Server Yürütme Süreleri:

CPU süresi = 31 ms, geçen süre = 235 ms.


1
Tam da aradığım şey buydu. FORMAT için resmi yardım: msdn.microsoft.com/es-MX/library/hh213505.aspx
Fer García

1
Bu,
MSDN'ye

5
Bu olsa da "en verimli" yolu olmayacak. Bu örnekte Biçim 180 saniye ile 12 saniye arasında değişmektedir. stackoverflow.com/a/27447244/73226
Martin Smith

2
Programcının kullanması için geçen süre açısından "en verimli" olabilir!
underscore_d

36

Birkaç kişi bunun versiyonlarını verdi:

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

buna dikkat edin çünkü n'den uzunsa gerçek verilerinizi kesecektir.


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

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

9

Belki de aşırı öldürme bu UDF'leri sağa ve sola doldurmak için kullanıyorum

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

ve sağa

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

Skaler UDF'lerle ilgili tek sorun, satır içi eşdeğer kod satırından çok daha kötü performans göstermeleridir (ayrıca veri türü sorunları vardır). Gelecekteki bir sürümde daha iyi skaler UDF performansı ve / veya satır içi skaler UDF'ler sunmalarını umuyoruz.
Cade Roux

Var uzunluğundan daha az bir uzunluk belirtirseniz, bu işlevler null değerini döndürür. Uzunluk daha azsa var döndürmek için yinelenen ifadelerin her birini isnull ifadesiyle sarın. isnull (çoğalt (...), '')
Jersey Dude

İşlev bildirimine SCHEMABINDING İLE eklenmesi, kullanıcı tanımlı işlevlerin verimliliğini artıracaktır. Bu, kaldırmak için zorlayıcı bir nedeniniz yoksa, varsayılan olarak tüm işlevlerinize eklemenizi tavsiye ettiğim bir şeydir.
EricI

7

Verdiğiniz yöntemin gerçekten verimsiz olduğundan emin değilim, ancak uzunluk veya dolgu karakterinde esnek olması gerekmediği sürece alternatif bir yol (" 0 "- 10 karakter arası:

DECLARE
   @pad_characters VARCHAR(10)

SET @pad_characters = '0000000000'

SELECT RIGHT(@pad_characters + @str, 10)

2

muhtemelen aşırıya kaçma, ben genellikle bu UDF kullanın:

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öylece aşağıdakileri yapabilirsiniz:

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

Bu aslında bazı verilere uymak için birkaç şey daha yapmak zorunda olan bir udf'de kullanılır.
Cade Roux

Bu, char ve varchar tiplerini birleştiriyorsanız da mükemmel çalışır.
Jimmy Baker

2

VnRocks çözümünü beğendim, burada bir udf biçiminde

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

sola yaslanmanın basit bir yolu:

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

xPad numarası ve ypad karakteri nerede .

örneklem:

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

Sen öyle ki sol dolgular numaraları hakkında konuşuyor gibi görünüyor 1olur 001.
Martin Smith


1

SQL Server 2005 ve sonraki sürümlerde bunu yapmak için bir CLR işlevi oluşturabilirsiniz.


1

Buna ne dersin:

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

3 zerosped sayısı


Bu bana yardımcı oldu: CONCAT (REPLACE (UZAY (@n - LENGTH (@str)), '', '0'), @str)
ingham

1

Umarım bu birine yardımcı olur.

STUFF ( character_expression , start , length ,character_expression )

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

0

Bunu kullanıyorum. Sonucun olmasını istediğiniz uzunluğu ve sağlanmamışsa varsayılan dolgu karakterini belirlemenizi sağlar. Elbette, giriş ve çıkışın uzunluğunu, çalıştığınız maksimum değerlere göre özelleştirebilirsiniz.

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

Kilometreniz değişebilir. :-)

Joey Morgan
Programcı / Analist Müdürü I
WellPoint Medicaid İş Birimi


0

İşte benim kesilmiş dizeleri önler ve düz ol 'SQL kullanan benim çözüm. Çözümleri bu kodun temeli olan @AlexCuse , @Kevin ve @Sklivvz sayesinde .

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

Bu, bu noktada konuşmaya fazla bir şey eklemediğini biliyorum ama bir dosya oluşturma prosedürü çalıştırıyorum ve inanılmaz derecede yavaş gidiyor. Replikat kullanıyorum ve bu trim yöntemini gördüm ve bir şans vereceğimi düşündüm.

Kodumda, yeni @padding değişkenine (ve şu anda var olan sınırlamaya) ek olarak ikisi arasındaki geçişin nerede olduğunu görebilirsiniz. Prosedürümü her iki durumda da işlevle aynı zamanda yürütme süresinde çalıştırdım. En azından SQLServer2016'da, diğerlerinin bulduğu verimlilikte herhangi bir fark görmüyorum.

Her neyse, işte yıllar önce yazdığım UDF'm artı bugün bir LEFT / RIGHT param seçeneği ve bazı hata kontrolleri dışında diğerleriyle aynı olan değişiklikler.

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

Ben x ondalık ile lpad bir işlevi var: CREATE FUNCTION [dbo]. [LPAD_DEC] (- Bu işlev için parametreleri ekleyin @pad nvarchar (MAX), @string nvarchar (MAX), @length int, @dec int ) GERİ DÖN nvarchar (maks.) BAŞLANGIÇ - Dönüş değişkenini burada bildirin DECLARE @resp nvarchar (maks.)

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

SON


-1

İki ondalık basamağa yuvarlanmış ancak gerekirse sıfırlarla sağlanmış sayısal değerler sağlamak için:

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)

Herkes daha temiz bir yol düşünebiliyorsa, bu takdir edilecektir - yukarıda sakar görünüyor .

Not : Bu örnekte, raporları HTML biçiminde e-postayla göndermek için SQL Server kullanıyorum ve bu nedenle verileri ayrıştırmak için ek bir araç içermeden bilgileri biçimlendirmek istiyorum.


1
SQL Server'ın türünü belirtmeden bir değişken bildirmenize izin verdiğini bilmiyordum. Her neyse, yönteminiz çalışmayan bir yöntem için "beceriksiz" görünüyor. :)
Andriy M

-4

İşte normalde bir varchar nasıl

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

14
Vay canına, bu inanılmaz derecede kötü.
Hogan

1
Döngüler, imleçler vb. Genellikle SQL'de kötüdür. Uygulama kodunda iyi olabilir, ancak SQL'de olmayabilir. Bazı istisnalar var ama bu onlardan biri değil.
Davos
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.