t-sql - 将函数应用于没有 sqlFunction 的列

t-sql - Apply function to a column without sqlFunction

我必须像这样将数字代码转换为字母代码:

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)

此函数运行良好,但在生产数据库中我们没有创建函数的权限。

我试过像这样将此函数转换为 CTE

declare @change varchar(9) = 'ABCDEFGHI'
DECLARE @II int = 0

;WITH x AS 
(
  SELECT TOP (10) n = ROW_NUMBER() OVER (ORDER BY Number)
  FROM master.dbo.spt_values ORDER BY Number
),
innerCTE as
(
    SELECT x.n, SUBSTRING(t.brand_code, x.n, 1) chnum, 
    case SUBSTRING(t.brand_code, x.n, 1)
        when '0' then '0'
        else char(65-1+SUBSTRING(t.brand_code, x.n, 1))
    end chalfa, t.brand_code
    FROM x INNER JOIN (VALUES('1234'),('5834'),('9905'),('0250')) as t(brand_code)
    ON x.n <= LEN(t.brand_code)
),
CTE as
(
    select n, chnum, chalfa, brand_code, stuff(brand_code, n, 1, chalfa) as code
    from innerCTE
    union all 
    select n+1, chnum, chalfa, brand_code, STUFF(code, n+1, 1, chalfa) as code
    from cte
    where n+1 <= LEN(cte.brand_code)
)
--select * from innerCTE
select * from CTE;

或者像这个例子一样使用 CROSS APPLY:

或者像这个例子一样使用 CROSS APPLY 和 PIVOT:Example_2

但是我在SQL上的经验很低,一直没能得到正确的结果。

我想要这个:

brand_code  decoded_code
1234        ABCD
5834        EHCD
9905        II0E
0250        0BE0

谢谢

以下解决方案使用 FOR XML 子查询和一些针对整数值的 mod (%) 操作来拆分每个数字,然后使用映射 table 来关联每个数字都有一个字符。 FOR XML returns 数字按顺序返回。

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

结果:

Integer     Conversion
1234        ABCD
6485834     FDHEHCD
99084705    II0HDG0E
1124601     AABDF0A

可能有更简洁的方法来编写映射和 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';

或者,如果是 2016 年或更早

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;

这是@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);