Найефективніший T-SQL спосіб накладати варчар зліва на певну довжину?


205

Порівняно з кажуть:

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

Я відкрутив останню редакцію. Питання дає один шлях - я шукав більш оптимальні шляхи. Правка втратила це значення в пошуках якоїсь іншої якості.
Кейд Ру

Відповіді:


323

Це просто неефективне використання SQL, незалежно від того, як ви це зробите.

можливо, щось подібне

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

де X - ваш символ прокладки, а @n - кількість символів у отриманій рядку (якщо припустити, що вам потрібна накладка, оскільки ви маєте справу з фіксованою довжиною).

Але, як я вже сказав, вам слід уникати цього робити у своїй базі даних.


2
Бувають випадки, коли це потрібно ... наприклад, отримання підмножини даних для екранованого сторінки.
Звуковий сигнал

7
+1 Просто випробували безліч різних методів, і це було найшвидше. Вам може знадобитися, RTRIM(@str)якщо це може містити пробіли.
Мартін Сміт

1
+1 Було спантеличено, чому мій знак char (6) не працює правильно, і RTRIM на вхідній змінній врятував мені кілька головок
jkelley

@MartinSmith Дякую ... Я намагався з'ясувати, чому мій рядок не працює, і rtrim виправив його. TY.
WernerCD

3
Це чудова відповідь. Звичайно, слід уникати цього, коли цього не потрібно, але іноді це неминуче; у моєму випадку я не маю вибору робити це в C # через обмеження розгортання, і хтось зберігав номер франшизи як INT, коли він повинен був бути числовим рядком з 5 символів з провідними нулями. Це надзвичайно допомогло.
Джим

57

Я знаю, що це спочатку запитували ще в 2008 році, але є деякі нові функції, які були введені у програмі SQL Server 2012. Функція FORMAT спрощує прокладку, залишену нулями. Він також здійснить перетворення для вас:

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

Оновлення:

Я хотів сам перевірити фактичну ефективність функції FORMAT. Я був дуже здивований, коли виявив, що ефективність була не дуже хорошою порівняно з оригінальною відповіддю від AlexCuse . Хоча я вважаю, що функція FORMAT чистіша, вона не дуже ефективна з точки зору часу виконання. Таблиця Tally, яку я використав, має 64 000 записів. Кудос Мартіну Сміту за вказівку на ефективність виконання часу.

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

Часи виконання SQL Server: час процесора = 2157 мс, минулий час = 2696 мс.

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

Часи виконання SQL Server:

Час процесора = 31 мс, минулий час = 235 мс.


1
Це саме те, що я шукав. Офіційна допомога для FORMAT: msdn.microsoft.com/es-MX/library/hh213505.aspx
Fer García

1
Це стосується SQL Server 2014 і далі відповідно до MSDN та мого власного досвіду його спробу на SQL Server 2012.
arame3333

5
Це не буде "найефективнішим" способом. У цьому прикладі Формат займає 180 секунд проти 12 секунд. stackoverflow.com/a/27447244/73226
Мартін Сміт

2
Це може бути "найефективнішим" з точки зору часу, який програміст повинен використати!
підкреслюю_d

36

Кілька людей дали версії цього:

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

будьте обережні з цим, оскільки він обріже ваші фактичні дані, якщо він довший за n.


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

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

9

Можливо, надмірне вбивство у мене є ці АДС, щоб прокладати ліворуч та праворуч

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

і праворуч

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

Єдина проблема скалярних UDF полягає в тому, що вони працюють набагато поганіше, ніж еквівалентний код вбудованого (плюс є проблеми з типом даних). Ось сподіваємось, що вони запровадять кращі скалярні показники АДС та / або вбудовані скалярні АДС в майбутній версії.
Кейд Ру

Якщо вказати довжину, меншу, ніж довжина var, ці функції повертаються до нуля. Оберніть кожне з повторюваних висловлювань оператором isnull, щоб просто повернути var, якщо довжина менша. isnull (копія (...), '')
Jersey Dude

Додавання функції "СХЕМАБІНДИНГ" до оголошення функції підвищить ефективність визначених користувачем функцій. Це те, що я рекомендую додавати за замовчуванням до всіх своїх функцій, якщо у вас немає вагомих причин видалити його.
EricI

7

Я не впевнений, що метод, який ви надаєте, насправді неефективний, але альтернативним способом, якщо він не повинен бути гнучким по довжині або символу прокладки, був би (якщо припустити, що ви хочете зробити його " 0 "до 10 символів:

DECLARE
   @pad_characters VARCHAR(10)

SET @pad_characters = '0000000000'

SELECT RIGHT(@pad_characters + @str, 10)

2

ймовірно, надмірність, я часто використовую цей 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

Щоб ви могли робити такі речі, як:

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

Це фактично використовується у udf, який повинен виконати кілька інших речей, щоб відповідати деяким даним.
Кейд Ру

Це також чудово працює, якщо ви поєднуєте типи чарів та варшарів.
Джиммі Бейкер

2

Мені сподобалося рішення vnRocks, ось воно у вигляді 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

це простий спосіб прокладки зліва:

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

Де xномер прокладки і yсимвол накладки.

зразок:

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

Ви, здається, говорите про ліві накладні номери такі, які 1стають 001.
Мартін Сміт


1

У SQL Server 2005 та пізніших версіях ви можете створити функцію CLR для цього.


1

Як щодо цього:

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

3 - число zerosдо колодки


У цьому мені допомогли: CONCAT (ЗАМІНА (SPACE (@n - LENGTH (@str)), '', '0'), @str)
ingham

1

Я сподіваюся, що це комусь допоможе.

STUFF ( character_expression , start , length ,character_expression )

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

0

Я використовую цей. Це дозволяє визначити довжину, яку ви хочете, щоб результат був, а також символ прокладки за замовчуванням, якщо такий не вказаний. Звичайно, ви можете налаштувати довжину вводу та виходу для будь-яких максимумів, з якими ви стикаєтесь.

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

Ваш пробіг може відрізнятися. :-)

Joey Morgan
Програміст / аналітик, директор I
WellPoint Medicaid Business Unit


0

Ось моє рішення, яке уникає усічених рядків і використовує звичайний ol 'SQL. Завдяки @AlexCuse , @Kevin та @Sklivvz , рішення яких є основою цього коду.

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

Я знаю, що це не додає багато розмови на даний момент, але я запускаю процедуру генерації файлів, і це відбувається дуже повільно. Я використовував репліку і бачив цей метод обрізки і подумав, що я б його сфотографував.

У моєму коді ви бачите, де перемикання між цими двома є додатково до нової змінної @padding (і обмеження, яке зараз існує). Я запустив свою процедуру з функцією в обох станах з однаковими результатами у часі виконання. Так що, принаймні, у SQLServer2016 я не бачу різниці в ефективності, яку знайшли інші.

У будь-якому випадку, ось мій UDF, про який я писав роки тому, а також зміни, які сьогодні такі ж, як і інші, ніж інші, мають параметр Вліво / Вправо парам і деякі перевірки помилок.

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

У мене є одна функція, яка lpad з x десятковими знаками: CREATE FUNCTION [dbo]. [LPAD_DEC] (- Додайте сюди параметри функції @pad nvarchar (MAX), @string nvarchar (MAX), @length int, @dec int ) ПОВЕРНУТИСЯ nvarchar (max) ЯК ПОЧАТО - оголосити тут змінну повернення 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

Кінець


-1

Щоб надати числові значення, округлені до двох знаків після коми, але праворуч зафіксовані нулями, якщо потрібно, я маю:

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)

Якщо хтось може придумати більш охайний спосіб, це було б вдячно - вищесказане здається незграбним .

Примітка . У цьому випадку я використовую SQL Server для надсилання звітів у форматі HTML, тому хочу відформатувати інформацію, не використовуючи додатковий інструмент для аналізу даних.


1
Не знав, що SQL Server дозволив вам оголосити змінну, не вказуючи її тип. У будь-якому випадку, ваш метод здається "незграбним" для непрацюючого. :)
Андрій М

-4

Ось як я б звичайно проклав варчар

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

14
Ого, це дивно погано.
Хоган

1
Петлі, курсори тощо - загалом погані в SQL. Можливо, це добре в коді програми, але не в SQL. Деякі винятки, але це не один з них.
Давос
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.