Sql server t-sql-将函数应用于不带sqlFunction的列

Sql server t-sql-将函数应用于不带sqlFunction的列,sql-server,tsql,common-table-expression,cross-apply,Sql Server,Tsql,Common Table Expression,Cross Apply,我必须将数字代码转换为字母代码,如下所示: 1234->ABCD 其中0=0、1=A、2=b等 这是我的功能和使用方法: Create function dbo.DecodeNumToChar ( @change varchar(10), @foo varchar(10) ) returns varchar(10) as begin DECLARE @II int = 1, @result varchar(10) ;WITH x AS

我必须将数字代码转换为字母代码,如下所示:

1234->ABCD

其中0=0、1=A、2=b等

这是我的功能和使用方法:

Create function dbo.DecodeNumToChar
(
    @change varchar(10),
    @foo varchar(10)
) returns varchar(10)
as
begin
    DECLARE @II int = 1,
            @result varchar(10)
    ;WITH x AS 
    (
        SELECT @II as ii, STUFF(@foo,@II,1,SUBSTRING(@change,CAST(SUBSTRING(@foo,@II,1) AS INT)+1,1)) AS AA
        UNION ALL
        --SELECT @II+1
        SELECT  ii+1, STUFF(AA,ii+1,1,SUBSTRING(@change,CAST(SUBSTRING(@foo,ii+1,1) AS INT)+1,1)) AS AA
        FROM x 
        where ii+1 <= LEN(@foo)
    )

    select top 1 @result = AA from x order by ii desc
    return @result
end
--------------------------------------------
select brand_code, dbo.DecodeNumToChar('0ABCDEFGHI', brand_code) 
from (VALUES('1234'),('5834'),('9905'),('0250')) as t(brand_code)

感谢

以下解决方案使用FOR XML子查询和对整数值的一些mod%操作来分割每个数字,然后使用映射表将每个数字与一个字符关联起来。FOR XML按顺序返回数字

DECLARE @IntegerValues TABLE (Integer INT)

INSERT INTO @IntegerValues (Integer)
VALUES
    (1234),
    (6485834),
    (99084705),
    (1124601)

SELECT
    T.Integer,
    Conversion = (
            SELECT
                '' + M.Character -- Must have no declared alias (for xml)
            FROM
                (VALUES
                    (1, T.Integer                       % 10),
                    (2, T.Integer /  10                 % 10),
                    (3, T.Integer /  100                % 10),
                    (4, T.Integer /  1000               % 10),
                    (5, T.Integer /  10000              % 10),
                    (6, T.Integer /  100000             % 10),
                    (7, T.Integer /  1000000            % 10),
                    (8, T.Integer /  10000000           % 10),
                    (9, T.Integer /  100000000          % 10),
                    (10, T.Integer / 1000000000         % 10),
                    (11, T.Integer / 10000000000        % 10)
                ) AS X(OrdinalPosition, SplitDigit)
                INNER JOIN (VALUES
                    (0, '0'),
                    (1, 'A'),
                    (2, 'B'),
                    (3, 'C'),
                    (4, 'D'),
                    (5, 'E'),
                    (6, 'F'),
                    (7, 'G'),
                    (8, 'H'),
                    (9, 'I')
                ) AS M(Digit, Character) ON X.SplitDigit = M.Digit
            WHERE
                X.OrdinalPosition <= FLOOR(LOG10(T.Integer)) + 1 -- This expression returns the number of digits
            ORDER BY
                X.OrdinalPosition DESC
            FOR XML
                PATH('')
        )
FROM
    @IntegerValues AS T

可能有一种更简洁的方法来编写映射和mod操作,但这应该会给出一个想法。

我已经找到了使用递归CTE的解决方案

declare @change varchar(10) = '0ABCDEFGHI'
DECLARE @II int = 1;

with BRANDS as
(
    select * from (VALUES('1234'),('5834'),('9905'),('0250')) as t(brand_code)
),
CTE as
(
    select  @II as ii, BRAND_CODE, 
            STUFF(brand_code,@II,1,SUBSTRING(@change,CAST(SUBSTRING(brand_code,@II,1) AS INT)+1,1)) AS AA
    from BRANDS
    union all
    select  c.ii+1, b.BRAND_CODE, 
            STUFF(AA,c.ii+1,1,SUBSTRING(@change,CAST(SUBSTRING(AA,c.ii+1,1) AS INT)+1,1)) AS AA
    from BRANDS b
    inner join CTE c on b.BRAND_CODE = c.BRAND_CODE
    where c.ii < LEN(b.BRAND_CODE)
)

select BRAND_CODE, AA as NewCode from CTE where ii = len(brand_code) order by BRAND_CODE, ii
如果使用SQL 2017+

DECLARE @integerValues TABLE ([I] INT);

INSERT INTO @integerValues ([I])
VALUES
    (1234),
    (6485834),
    (99084705),
    (1124601);

SELECT
            T.[I],
            STRING_AGG(
                CASE SUBSTRING(T.[S], V.[number] + 1, 1)
                    WHEN '9' THEN 'I'
                    WHEN '8' THEN 'H'
                    WHEN '7' THEN 'G'
                    WHEN '6' THEN 'F'
                    WHEN '5' THEN 'E'
                    WHEN '4' THEN 'D'
                    WHEN '3' THEN 'C'
                    WHEN '2' THEN 'B'
                    WHEN '1' THEN 'A'
                    ELSE '0'
                END,
                '') WITHIN GROUP (ORDER BY T.[I]) [S]
    FROM
            (SELECT [I], CAST([I] AS VARCHAR(10)) [S] FROM @integerValues) T
        JOIN
            [master]..[spt_values] V ON V.[number] < LEN(T.[S])
    WHERE
        V.[type] = 'P';

这是@Martin Smith建议使用10嵌套替换的代码

DECLARE @change char(10) = '0ABCDEFGHI';
--------------------------------------------
select brand_code,
    REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( 
    REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( brand_code
    , '0', SUBSTRING( @change, 1, 1)) , '1', SUBSTRING( @change, 2, 1)) 
    , '2', SUBSTRING( @change, 3, 1)) , '3', SUBSTRING( @change, 4, 1)) 
    , '4', SUBSTRING( @change, 5, 1)) , '5', SUBSTRING( @change, 6, 1)) 
    , '6', SUBSTRING( @change, 7, 1)) , '7', SUBSTRING( @change, 8, 1)) 
    , '8', SUBSTRING( @change, 9, 1)) , '9', SUBSTRING( @change, 10, 1)) 
from (VALUES('1234'),('5834'),('9905'),('0250')) as t(brand_code);

我只想使用10 nested REPLACE,假设您所使用的版本实际上没有Decode。Martin的提示是最简单和直接的。看起来使用@Jodrell是个好时机,您肯定是对的,但我不知道我是否可以在PROD Environment上使用它。Hi@EzLo,感谢您的解决方案,它正在工作。同时,我还解决了另一个查询,您可以在我的answerRecursion+中阅读,如果您需要一次性转换数千条记录,那么递归+字符串函数的性能可能会受到影响。尝试具有大量测试值的解决方案,看看它是否符合您的要求。另外,请尝试一下Martin的建议,它非常简单,可能是目前发布的最快的解决方案。@EzLo我明白了。。。所以在我的例子中,有300条记录和代码,长度为3位数:-@GlaucoCucchiar如果这是一次性操作,则无所谓。如果您经常需要这个,并且长度不超过3位,我会考虑一个从0到999的物理表,代码在第二列中。生成一次,并通过将其加入到集合中来使用它…我不确定为什么要使用子字符串。我使用它只是为了防止用于更改值的字符串发生更改。如果值保持不变,我同意硬编码值就足够了,我们可以删除子字符串。
declare @change varchar(10) = '0ABCDEFGHI'
DECLARE @II int = 1;

with BRANDS as
(
    select * from (VALUES('1234'),('5834'),('9905'),('0250')) as t(brand_code)
),
CTE as
(
    select  @II as ii, BRAND_CODE, 
            STUFF(brand_code,@II,1,SUBSTRING(@change,CAST(SUBSTRING(brand_code,@II,1) AS INT)+1,1)) AS AA
    from BRANDS
    union all
    select  c.ii+1, b.BRAND_CODE, 
            STUFF(AA,c.ii+1,1,SUBSTRING(@change,CAST(SUBSTRING(AA,c.ii+1,1) AS INT)+1,1)) AS AA
    from BRANDS b
    inner join CTE c on b.BRAND_CODE = c.BRAND_CODE
    where c.ii < LEN(b.BRAND_CODE)
)

select BRAND_CODE, AA as NewCode from CTE where ii = len(brand_code) order by BRAND_CODE, ii
DECLARE @integerValues TABLE ([I] INT);

INSERT INTO @integerValues ([I])
VALUES
    (1234),
    (6485834),
    (99084705),
    (1124601);

SELECT
            T.[I],
            STRING_AGG(
                CASE SUBSTRING(T.[S], V.[number] + 1, 1)
                    WHEN '9' THEN 'I'
                    WHEN '8' THEN 'H'
                    WHEN '7' THEN 'G'
                    WHEN '6' THEN 'F'
                    WHEN '5' THEN 'E'
                    WHEN '4' THEN 'D'
                    WHEN '3' THEN 'C'
                    WHEN '2' THEN 'B'
                    WHEN '1' THEN 'A'
                    ELSE '0'
                END,
                '') WITHIN GROUP (ORDER BY T.[I]) [S]
    FROM
            (SELECT [I], CAST([I] AS VARCHAR(10)) [S] FROM @integerValues) T
        JOIN
            [master]..[spt_values] V ON V.[number] < LEN(T.[S])
    WHERE
        V.[type] = 'P';
DECLARE @integerValues TABLE ([I] INT);

INSERT INTO @integerValues ([I])
VALUES
    (1234),
    (6485834),
    (99084705),
    (1124601);

SELECT
        [I],
        REPLACE(
            REPLACE(
                REPLACE(
                    REPLACE(
                        REPLACE(
                            REPLACE(
                                REPLACE(
                                    REPLACE(
                                        REPLACE(
                                            CAST([I] AS VARCHAR(10)),
                                            '1',
                                            'A'),
                                        '2',
                                        'B'),
                                    '3',
                                    'C'),
                                '4',
                                'D'),
                            '5',
                            'E'),
                        '6',
                        'F'),
                    '7',
                    'G'),
                '8',
                'H'),
            '9',
            'I') [S]
    FROM
        @integerValues;
DECLARE @change char(10) = '0ABCDEFGHI';
--------------------------------------------
select brand_code,
    REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( 
    REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( brand_code
    , '0', SUBSTRING( @change, 1, 1)) , '1', SUBSTRING( @change, 2, 1)) 
    , '2', SUBSTRING( @change, 3, 1)) , '3', SUBSTRING( @change, 4, 1)) 
    , '4', SUBSTRING( @change, 5, 1)) , '5', SUBSTRING( @change, 6, 1)) 
    , '6', SUBSTRING( @change, 7, 1)) , '7', SUBSTRING( @change, 8, 1)) 
    , '8', SUBSTRING( @change, 9, 1)) , '9', SUBSTRING( @change, 10, 1)) 
from (VALUES('1234'),('5834'),('9905'),('0250')) as t(brand_code);