将以10为底的整数转换为以3为底,加上以3为底的数字,然后将结果转换回以10为底

问题描述

我需要创建一个Microsoft T-sql函数,该函数需要一个以10为底的整数(例如20201)和另一个输入整数,将它们都转换为以3为底,将它们加在一起,然后以10为底返回结果。

我们正在使用它来计算学生将来的学期数。我们的术语表示为20201 =春季,20202 =夏季,20203 =秋季,等等。我们希望将一个起始术语传递给该函数,并将其传递给将来的术语数量,并将结果返回给未来的术语。 / p>

示例:

20183 starting term,1 terms in the future = 20191
20183 starting term,2 terms in the future = 20192
20183 starting term,3 terms in the future = 20193
20201 starting term,1 terms in the future = 20202
20201 starting term,2 terms in the future = 20203
20201 starting term,9 terms in the future = 20231 (20202=1,20203=2,20211=3,20212=4,20213=5,20221=6,20222=7,20223=8,20231=9)

到目前为止,我已经找到了一些示例,这些示例可以将数字转换为其他基数,但不能使用不在base10中的输入将其转换回base10

我目前拥有以下内容,该内容对于20201作为起始术语可以正常工作,但是使用20183作为起始术语会产生错误的结果。下面的示例已修改,但尚未从在线其他地方找到的示例(还可以处理直到36的基数更改)中完成,这就是为什么要进行字符串转换的原因。

ALTER FUNCTION [dbo].[AddTermsToBaseTerm] 
    (@startTerm NVARCHAR(5),@termsToAdd INT)
RETURNS INT
AS
BEGIN
     DECLARE @base3 INT = 3 --base 3 is how we count terms (e.g.,20201,20202,202023,20211,20212,20213,20221)
     DECLARE @dividend INT = @termsToAdd,@remainder INT = 0,@numberString VARCHAR(255) = CASE WHEN @termsToAdd = 0 THEN '0' ELSE '' END ;

     SET @base3 = CASE WHEN @base3 <= 36 THEN @base3 ELSE 36 END;--The max base is 36,includes the range of [0-9A-Z]

     WHILE (@dividend > 0 OR @remainder > 0)
     BEGIN
         SET @remainder = @dividend % @base3 ; --The reminder by the division number in base
         SET @dividend = @dividend / @base3 ; -- The integer part of the division,becomes the new dividend for the next loop

         IF(@dividend > 0 OR @remainder > 0)--check that not correspond the last loop when quotient and reminder is 0
             SET @numberString =  CHAR( (CASE WHEN @remainder <= 9 THEN ASCII('0') ELSE ASCII('A')-10 END) + @remainder ) + @numberString; --original
     END;

     RETURN CAST(@startTerm AS INT) + CAST(@numberString AS INT);
END
GO

SELECT CAST(dbo.AddTermsToBaseTerm('20183',2) AS INT) AS 'futureterm' --incorrect result
SELECT CAST(dbo.AddTermsToBaseTerm('20183',2) AS INT) AS 'futureterm' --incorrect result
SELECT CAST(dbo.AddTermsToBaseTerm('20201',2) AS INT) AS 'futureterm'
SELECT CAST(dbo.AddTermsToBaseTerm('20201',3) AS INT) AS 'futureterm'
SELECT CAST(dbo.AddTermsToBaseTerm('20201',4) AS INT) AS 'futureterm'
SELECT CAST(dbo.AddTermsToBaseTerm('20201',5) AS INT) AS 'futureterm'
SELECT CAST(dbo.AddTermsToBaseTerm('20201',6) AS INT) AS 'futureterm'

解决方法

-??

declare @startterm int = 20183,@addterms smallint = 3;

select (@startterm/10 + (@addterms+@startterm%10-1)/3) * 10 + isnull(nullif((@addterms+@startterm%10)%3,0),3) as endterm;
,

这是一个表值函数(tvf),它将在未来最多256个三个月内进行投影。它使用数字表(或计数表)和序列的模数:)来查看其工作方式,以免注释掉组件列。

drop function if exists dbo.fnTermsToAdd;
go
create function dbo.fnTermsToAdd(
  @startTerm            NVARCHAR(5),@termsToAdd           INT)
returns table with schemabinding as 
return
WITH
  H2(N) AS ( SELECT 1 
               FROM (VALUES
                     (1),(1),(1)
                    )V(N)),rn_cte as (SELECT TOP(@termsToAdd) row_number() over (order by n) N FROM H2)
select concat(cast(cast(left(@startTerm,4) as int)+(N-1)/3 as nchar(4)),cast(N%3+1 as nchar(1))) trimester
/*
  cast(left(@startTerm,4) as int) yr,cast(right(@startTerm,1) as int) tri,(N-1)/3 tri_add,N%3+1 seq_add 
*/
from rn_cte
where not ((N-1)/3=0 and N%3+1<cast(right(@startTerm,1) as int));
go

像这样执行它

select * from dbo.fnTermsToAdd('20202',21) order by 1;

结果

trimester
20202
20203
20211
20212
20213
20221
20222
20223
20231
20232
20233
20241
20242
20243
20251
20252
20253
20261
20262
20263

要从“ 20201”开始获取未来9个学期的术语,请使用MAX函数

select max(trimester) term from dbo.fnTermsToAdd('20201',9);

结果

term
20223
,

一种选择是将数字转换为包含的术语数量,添加相关数字,然后转换回原始格式。

简单的算术函数(整数除法和模)可以按以下方式完成此操作:

declare @startTerm int = 20201,@termsToAdd int = 9;

declare @totalTerms int = (@startTerm / 10) * 3 + @startTerm % 10 + @termsToAdd;
declare @entTerm int = (@totalTerms / 3) * 10  + @totalTerms % 3 + 1;

变量endTerm包含计算结果。