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);
我必须像这样将数字代码转换为字母代码:
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);