1

我在 SQL 2008R2 中编写了一个函数,我可以给它一些参数,比如值 varchar、模式 varchar、分隔符和填充符。然后我想用模式'000.000.000.000',分隔符'.'给出值'22687' 并且填充符将为“0”,那么我希望该函数将返回“000.000.022.687”,是否有人已经完成了可以执行此操作的函数?

像这样的东西:

DECLARE @valor VARCHAR(30)
DECLARE @formato VARCHAR(30)
DECLARE @separador CHAR(1) 
DECLARE @rellenarcon CHAR(1)

SELECT @valor       = '22959'
SELECT @formato     = '000.000.000.000'
SELECT @separador   = '.'
SELECT @rellenarcon = '0'

DECLARE @n  INTEGER
DECLARE @m  INTEGER
DECLARE @i  INTEGER
DECLARE @j  INTEGER

SELECT @n   = LEN(@formato)
SELECT @m   = LEN(@valor)
SELECT @i   = 1
SELECT @j   = 1

DECLARE @res2 varchar(30)

SELECT @res2 = ''
SELECT @valor = REVERSE(@valor)

WHILE @i<=@n
BEGIN
    if SUBSTRING(@formato,@i,1) <> @separador
    begin
        IF @j<=@m
        BEGIN
            SELECT @res2 = @res2 + SUBSTRING(@valor,@j,1)
            SELECT @i=@i+1
            SELECT @j=@j+1
        END
        ELSE
        BEGIN
            SELECT @res2 = @res2 + @rellenarcon
            SELECT @i=@i+1      
        END
    end
    else
    BEGIN
        SELECT @res2 = @res2 + @separador
        SELECT @i=@i+1
    END
END

print reverse(@res2)

是从java到tsql的交叉代码,java中的原代码是:

    public static String formatear(String valor, String formato, char separator, 
        char fillWith, Map<Integer, String> params) {

    int n = formato.length() - 1;
    int m = valor.length() - 1;
    int i = n;
    int j = m;

    StringBuilder res = new StringBuilder(formato);

    for(; i >= 0; i--) {
        if(res.charAt(i) != separator) {
            if(j >= 0) {
                res.deleteCharAt(i);
                res.insert(i, valor.charAt(j--));
            } else {
                res.deleteCharAt(i);
                res.insert(i, fillWith);
            }
        }
    }
    if(params != null) {
        Set<Integer> keys = params.keySet();

        for(Integer key : keys) {
            i = key;
            res.deleteCharAt(i);
            res.insert(i, params.get(key));
        }
    }

    return res.toString();
}
4

3 回答 3

0

此查询将执行此操作:

;with cteZeroPadded(Num) as
(
    Select  Right('000000000000' + '22687', 12)
)
,cteSplit as
(
    Select  SUBSTRING(Num, 1, 3) Col1
            ,SUBSTRING(Num, 4, 3) Col2
            ,SUBSTRING(Num, 7, 3) Col3
            ,SUBSTRING(Num, 10, 3) Col4
    From    cteZeroPadded
)
Select  Col1 + '.' + Col2 + '.' + Col3 + '.' + Col4
From    cteSplit
于 2013-05-08T20:24:46.350 回答
0

在 SQLServer2005+ 中,您可以使用带有递归CTE的选项

DECLARE @valor varchar(30) = '22959',
        @formato varchar(30) = '000000000000000',
        @text varchar(30),
        @result varchar(30) = N''

SET @text = REVERSE(RIGHT(@formato + @valor, 15))

;WITH cte AS
  (  
   SELECT 1 AS Number, SUBSTRING(@text, 1, 1) AS Num
   UNION ALL
   SELECT c.Number + 1,
          CASE WHEN c.Number IN(3, 7, 11) THEN '.' ELSE
          SUBSTRING(@text, CASE WHEN c.Number > 11 THEN c.Number - 2
                                WHEN c.Number > 7 THEN c.Number - 1
                                WHEN c.Number > 3 THEN c.Number
                                ELSE c.Number + 1
                           END, 1)
          END
   FROM cte c
   WHERE Number < LEN(@text)
   )   
   SELECT @result += c.Num
   FROM cte c
   ORDER BY Number DESC

SELECT @result

请参阅SQLFiddle上的演示

于 2013-05-08T22:23:23.607 回答
0

以下假设输入格式正确,例如该值不长于模式。

declare @Pattern as VarChar(64) = '000.000.000.000';
declare @Fill as Char = '0';
declare @Value as VarChar(64) = '22687';
declare @False as Bit = 0;
declare @True as Bit = 1;

with Gargoyle as (
  select @Pattern as Pattern, @Value as Value, Cast( '' as VarChar(64) ) as Buffer,
    case when Right( @Pattern, 1 ) = @Fill then @True else @False end as Fill
  union all
  select
    -- Always consume a character from the pattern.
    Left( Pattern, Len( Pattern ) - 1 ),
    -- Consume a character from the value if the pattern contains fill at the current position.
    case
      when Fill = @True and Value != '' then Left( Value, Len( Value ) - 1 )
      else Value end,
    -- Add the correct character to the buffer.
    Cast( case when Fill = @True and Value != '' then Right( Value, 1 ) else Right( Pattern, 1 ) end + Buffer as VarChar(64) ),
    -- Check the next pattern character for fill.
    case
      when Len( Pattern ) = 1 then @False
      when Substring( Pattern, Len( Pattern ) - 1, 1 ) = @Fill then @True
      else @False end
    from Gargoyle
    where Pattern != ''
  )
  select Buffer
    from Gargoyle
    where Pattern = '';

或者,作为一个函数:

create function dbo.PatternFill( @Pattern as VarChar(64), @Fill as Char, @Value as VarChar(64) )
  returns VarChar(64)
  as
  begin
  declare @Buffer as VarChar(64) = ''
  declare @PatternChar as Char = Right( @Pattern, 1 )
  declare @ValueChar as Char = Right( @Value, 1 )
  while @Pattern != ''
    begin
    if @PatternChar = @Fill and @ValueChar != ''
      begin
      -- Replace a fill character with a value character.
      select @Buffer = @ValueChar + @Buffer
      if Len( @Value ) > 1
        select @Value = Left( @Value, Len( @Value ) - 1 ), @ValueChar = Right( @Value, 1 )
      else
        select @ValueChar = '', @Value = ''
      end
    else
      begin
      -- Copy the pattern character.
      select @Buffer = @PatternChar + @Buffer
      end
    if Len( @Pattern ) > 1
      select @Pattern = Left( @Pattern, Len( @Pattern ) - 1 ), @PatternChar = Right( @Pattern, 1 )
    else
      select @PatternChar = '', @Pattern = ''
    end
  return @Buffer
  end
go

declare @Result as VarChar(64)
declare @Count as Int = 1000000
declare @Start as DateTime = GetDate()
while @Count > 0
  select @Result = dbo.PatternFill( '000.000.000.000', '0', '22687' ), @Count = @Count - 1
select @Result as [Result], DateDiff( ms, @Start, GetDate() ) as [Total ms]

我的笔记本上 1,000,000 次迭代花费了 151,656 毫秒,但它正忙于 BOINCing。这是一个简单的计时,没有校正空循环或调用空函数所消耗的时间。

于 2013-05-09T02:41:25.467 回答