Die effizienteste T-SQL-Methode, um einen Varchar links auf eine bestimmte Länge aufzufüllen?


205

Im Vergleich zu sagen:

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

Ich habe die letzte Bearbeitung zurückgesetzt. Die Frage gibt einen Weg - ich suchte nach optimaleren Wegen. Die Bearbeitung verlor diese Implikation auf der Suche nach einer anderen Qualität.
Cade Roux

Antworten:


323

Dies ist einfach eine ineffiziente Verwendung von SQL, egal wie Sie es tun.

vielleicht so etwas wie

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

Dabei ist X Ihr Auffüllzeichen und @n die Anzahl der Zeichen in der resultierenden Zeichenfolge (vorausgesetzt, Sie benötigen das Auffüllen, weil Sie es mit einer festen Länge zu tun haben).

Aber wie gesagt, Sie sollten dies in Ihrer Datenbank wirklich vermeiden.


2
Es gibt Zeiten, in denen dies erforderlich ist ... zum Beispiel das Abrufen einer Teilmenge von Daten für einen ausgelagerten Bildschirm.
Beep Beep

7
+1 Habe gerade eine Menge verschiedener Methoden getestet und dies war die schnellste. Möglicherweise müssen Sie dies jedoch RTRIM(@str)tun, wenn dies nachgestellte Leerzeichen enthalten kann.
Martin Smith

1
+1 war verwirrt darüber, warum mein Zeichen (6) nicht richtig aufgefüllt wurde, und das RTRIM auf der Eingabevariablen ersparte mir einige
Kopfkratzer

@MartinSmith Danke ... Ich habe versucht herauszufinden, warum meine Saite nicht funktioniert hat und rtrim hat sie behoben. TY.
WernerCD

3
Dies ist eine großartige Antwort. Natürlich sollten Sie dies vermeiden, wenn Sie es nicht müssen, aber manchmal ist es unvermeidlich; In meinem Fall habe ich aufgrund von Bereitstellungsbeschränkungen nicht die Wahl, dies in C # zu tun, und jemand hat eine Franchise-Nummer als INT gespeichert, wenn es sich um eine 5-stellige numerische Zeichenfolge mit führenden Nullen handeln sollte. Dies hat immens geholfen.
Jim

57

Ich weiß, dass dies ursprünglich im Jahr 2008 gefragt wurde, aber es gibt einige neue Funktionen, die mit SQL Server 2012 eingeführt wurden. Die FORMAT-Funktion vereinfacht das Auffüllen mit Nullen. Es wird auch die Konvertierung für Sie durchführen:

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

Aktualisieren:

Ich wollte die tatsächliche Effizienz der FORMAT-Funktion selbst testen. Ich war ziemlich überrascht, dass die Effizienz im Vergleich zur ursprünglichen Antwort von AlexCuse nicht sehr gut war . Obwohl ich die FORMAT-Funktion sauberer finde, ist sie in Bezug auf die Ausführungszeit nicht sehr effizient. Die von mir verwendete Tally-Tabelle enthält 64.000 Datensätze. Ein großes Lob an Martin Smith für den Hinweis auf die Effizienz der Ausführungszeit.

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

SQL Server-Ausführungszeiten: CPU-Zeit = 2157 ms, verstrichene Zeit = 2696 ms.

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

SQL Server-Ausführungszeiten:

CPU-Zeit = 31 ms, verstrichene Zeit = 235 ms.


1
Genau das habe ich gesucht. Offizielle Hilfe für FORMAT: msdn.microsoft.com/es-MX/library/hh213505.aspx
Fer García

1
Dies gilt für SQL Server 2014 ab MSDN und meiner eigenen Erfahrung mit SQL Server 2012.
arame3333

5
Dies wird jedoch nicht der "effizienteste" Weg sein, wie gefragt. In diesem Beispiel dauert das Format 180 Sekunden gegenüber 12 Sekunden. stackoverflow.com/a/27447244/73226
Martin Smith

2
Es kann in Bezug auf die Zeit, die der Programmierer benötigt, um es zu nutzen, "am effizientesten" sein!
underscore_d

36

Mehrere Leute gaben Versionen davon:

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

Seien Sie vorsichtig damit, da Ihre tatsächlichen Daten abgeschnitten werden, wenn sie länger als n sind.


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

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

9

Vielleicht ein Over-Kill Ich habe diese UDFs, um links und rechts aufzufüllen

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

und nach rechts

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

Das einzige Problem mit skalaren UDFs besteht darin, dass sie weitaus schlechter als gleichwertiger Code inline arbeiten (und es gibt Probleme mit dem Datentyp). Wir hoffen, dass sie in einer zukünftigen Version eine bessere skalare UDF-Leistung und / oder skalare Inline-UDFs einführen.
Cade Roux

Wenn Sie eine Länge angeben, die kleiner als die Länge von var ist, geben diese Funktionen null zurück. Umschließen Sie jede der Replikationsanweisungen mit einer isnull-Anweisung, um einfach var zurückzugeben, wenn die Länge geringer ist. isnull (replizieren (...), '')
Jersey Dude

Durch Hinzufügen von WITH SCHEMABINDING zur Funktionsdeklaration wird die Effizienz der benutzerdefinierten Funktionen verbessert. Ich empfehle, dies standardmäßig allen Ihren Funktionen hinzuzufügen, es sei denn, Sie haben einen zwingenden Grund, es zu entfernen.
EricI

7

Ich bin mir nicht sicher, ob die von Ihnen angegebene Methode wirklich ineffizient ist, aber eine alternative Methode, sofern sie nicht in der Länge oder im Auffüllcharakter flexibel sein muss, wäre (vorausgesetzt, Sie möchten sie auffüllen ". 0 "bis 10 Zeichen:

DECLARE
   @pad_characters VARCHAR(10)

SET @pad_characters = '0000000000'

SELECT RIGHT(@pad_characters + @str, 10)

2

wahrscheinlich übertrieben, ich benutze oft diese 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

Damit Sie Dinge tun können wie:

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

Dies wird tatsächlich in einem udf verwendet, das einige andere Dinge tun muss, um einige Daten anzupassen.
Cade Roux

Dies funktioniert auch perfekt, wenn Sie Char- und Varchar-Typen kombinieren.
Jimmy Baker

2

Ich mochte die vnRocks-Lösung, hier ist sie in Form eines 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

Dies ist eine einfache Möglichkeit, links aufzufüllen:

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

Wo xist die Pad-Nummer undy ist das Pad-Zeichen.

Stichprobe:

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

Sie scheinen über links padding Zahlen zu sprechen , so dass 1wird 001.
Martin Smith


1

In SQL Server 2005 und höher können Sie dazu eine CLR-Funktion erstellen.


1

Wie wäre es damit:

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

3 ist die Nummer des zerosPads


Dies hat mir geholfen: CONCAT (REPLACE (SPACE (@n - LENGTH (@str)), '', '0'), @str)
Ingham

1

Ich hoffe das hilft jemandem.

STUFF ( character_expression , start , length ,character_expression )

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

0

Ich benutze diesen. Hier können Sie die Länge bestimmen, in der das Ergebnis angezeigt werden soll, sowie ein Standard-Füllzeichen, falls keines angegeben ist. Natürlich können Sie die Länge der Ein- und Ausgabe für jedes Maximum anpassen, auf das Sie stoßen.

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

Ihr Kilometerstand kann variieren. :-)

Joey Morgan
Programmierer / Analyst Principal I
Geschäftsbereich WellPoint Medicaid


0

Hier ist meine Lösung, die abgeschnittene Zeichenfolgen vermeidet und einfaches altes SQL verwendet. Vielen Dank an @AlexCuse , @Kevin und @Sklivvz , deren Lösungen die Grundlage dieses Codes bilden.

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

Ich weiß, dass dies zu diesem Zeitpunkt nicht viel zur Konversation beiträgt, aber ich führe eine Prozedur zur Dateierzeugung aus, die unglaublich langsam verläuft. Ich habe Replikat verwendet und diese Trimmmethode gesehen und dachte, ich würde es versuchen.

Sie können in meinem Code sehen, wo der Wechsel zwischen den beiden zusätzlich zu der neuen @ padding-Variablen (und der jetzt bestehenden Einschränkung) erfolgt. Ich habe meine Prozedur mit der Funktion in beiden Zuständen mit den gleichen Ergebnissen in der Ausführungszeit ausgeführt. Zumindest in SQLServer2016 sehe ich keinen Unterschied in der Effizienz, den andere festgestellt haben.

Wie auch immer, hier ist meine UDF, die ich vor Jahren geschrieben habe, sowie die Änderungen heute, die denen anderer sehr ähnlich sind, außer dass sie eine LINKS / RECHTS-Parameteroption und einige Fehlerprüfungen enthalten.

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

Ich habe eine Funktion, die mit x Dezimalstellen lpad: CREATE FUNCTION [dbo]. [LPAD_DEC] (- Fügen Sie hier die Parameter für die Funktion hinzu @pad nvarchar (MAX), @string nvarchar (MAX), @length int, @dec int ) RETURNS nvarchar (max) AS BEGIN - Deklarieren Sie die Rückgabevariable hier 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

ENDE


-1

Um numerische Werte bereitzustellen, die auf zwei Dezimalstellen gerundet, aber bei Bedarf mit Nullen rechts aufgefüllt sind, habe ich:

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)

Wenn sich jemand einen ordentlicheren Weg vorstellen kann, wäre das wünschenswert - das oben Gesagte scheint ungeschickt zu sein .

Hinweis : In diesem Fall verwende ich SQL Server, um Berichte im HTML-Format per E-Mail zu versenden, und möchte daher die Informationen formatieren, ohne ein zusätzliches Tool zum Parsen der Daten zu verwenden.


1
Ich wusste nicht, dass Sie mit SQL Server eine Variable deklarieren können, ohne ihren Typ anzugeben. Wie auch immer, Ihre Methode scheint für eine nicht funktionierende Methode "ungeschickt" zu sein. :)
Andriy M

-4

Hier ist, wie ich normalerweise einen Varchar auffüllen würde

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

14
Wow, das ist unglaublich schlecht.
Hogan

1
Schleifen, Cursor usw. sind in SQL im Allgemeinen schlecht. Könnte im Anwendungscode in Ordnung sein, aber nicht in SQL. Einige Ausnahmen, aber dies ist keine davon.
Davos
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.