web-dev-qa-db-fra.com

Le moyen T-SQL le plus efficace pour caler un varchar à gauche d’une certaine longueur?

Par rapport à dire:

REPLICATE(@padchar, @len - LEN(@str)) + @str
201
Cade Roux

Ceci est simplement une utilisation inefficace de SQL, peu importe comment vous le faites.

peut-être quelque chose comme

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

où X est votre caractère de remplissage et @n le nombre de caractères de la chaîne résultante (en supposant que vous ayez besoin du remplissage parce que vous traitez avec une longueur fixe).

Mais comme je l'ai dit, vous devriez vraiment éviter de le faire dans votre base de données.

319
AlexCuse

Je sais que cela avait été demandé à l'origine en 2008, mais de nouvelles fonctions ont été introduites avec SQL Server 2012. Le fonction FORMAT simplifie bien le remplissage à gauche avec des zéros. Il effectuera également la conversion pour vous:

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

Mise à jour:

Je voulais tester l'efficacité réelle de la fonction FORMAT moi-même. J'ai été assez surpris de constater que l'efficacité n'était pas très bonne comparée à la réponse originale de AlexCuse . Bien que je trouve la fonction FORMAT plus propre, elle n’est pas très efficace en termes de temps d’exécution. La table de comptage que j'ai utilisée contient 64 000 enregistrements. Félicitations à Martin Smith pour avoir signalé l'efficacité du temps d'exécution.

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

Temps d'exécution SQL Server: temps CPU = 2157 ms, temps écoulé = 2696 ms.

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

Temps d'exécution de SQL Server:

Temps CPU = 31 ms, temps écoulé = 235 ms.

54
jediCouncilor

Plusieurs personnes ont donné des versions de ceci:

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

soyez prudent avec cela car il tronquera vos données réelles si elles sont plus longues que n.

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

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

Peut-être un excès de mort, j'ai ces FDU à pad gauche et à droite

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

et à droite

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
9
TonyP

Je ne suis pas sûr que la méthode que vous donnez soit vraiment inefficace, mais une autre méthode, dans la mesure où elle ne doit pas être flexible dans la longueur ou le caractère de remplissage, serait (en supposant que vous souhaitiez la remplir avec " 0 "à 10 caractères:

DECLARE
   @pad_characters VARCHAR(10)

SET @pad_characters = '0000000000'

SELECT RIGHT(@pad_characters + @str, 10)
7
Tom H

J'ai aimé la solution vnRocks, la voici sous la forme d'un fichier 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
Kevin

probablement exagéré, j’utilise souvent ce format 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

Pour que vous puissiez faire des choses comme:

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

c'est un moyen simple de taper à gauche:

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

x est le numéro du pad et y est le caractère du pad.

échantillon:

REPLACE(STR(FACT_HEAD.FACT_NO, 3, 0), ' ', 0)
2
Ahmad
select right(replicate(@padchar, @len) + @str, @len)
1
Gordon Bell

Dans SQL Server 2005 et versions ultérieures, vous pouvez créer une fonction CLR à cet effet.

1
Kramii

J'espère que ça aidera quelqu'un.

STUFF ( character_expression , start , length ,character_expression )

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

Que dis-tu de ça:

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

3 est le nombre de zeros to pad

1
joshblair

J'ai une fonction qui lpad avec x décimales: CREATE FUNCTION [dbo]. [LPAD_DEC] (- Ajoutez les paramètres pour la fonction ici @pad nvarchar (MAX), @string nvarchar (MAX), @length int, @dec int ) RETURNS nvarchar (max) AS DEBUT - Déclarez la variable de retour ici 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

FIN

0
Pancho R

J'utilise celui-ci. Il vous permet de déterminer la longueur souhaitée du résultat, ainsi qu'un caractère de remplissage par défaut, le cas échéant. Bien sûr, vous pouvez personnaliser la longueur de l’entrée et de la sortie en fonction du maximum que vous rencontrez.

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

Votre kilométrage peut varier. :-)

Joey Morgan
Programmeur/analyste principal I
Unité commerciale WellPoint Medicaid

0
Joseph Morgan

Je sais que cela n’ajoute pas grand chose à la conversation à ce stade-ci, mais j’exécute une procédure de génération de fichier et sa lenteur est incroyable. Je me suis servi de répliquer et j'ai vu cette méthode de rognage et je me suis dit que j'essaierais.

Vous pouvez voir dans mon code où le passage entre les deux est en plus de la nouvelle variable @padding (et de la limitation qui existe maintenant). J'ai exécuté ma procédure avec la fonction dans les deux états avec les mêmes résultats en temps d'exécution. Donc, au moins dans SQLServer2016, je ne vois aucune différence d'efficacité constatée par les autres.

Quoi qu'il en soit, voici mon fichier UDF que j'ai écrit il y a des années, ainsi que les modifications actuelles, qui sont sensiblement les mêmes que celles des autres logiciels, à l'exception du fait qu'il dispose d'une option param gauche/droite et d'une vérification des erreurs.

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
blind Skwirl

Voici ma solution, qui évite les chaînes tronquées et utilise un langage SQL clair. Merci à @ AlexCuse, @ Kevin et @ Sklivvz, dont les solutions sont la base de ce 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;
0
Mass Dot Net