将以 10 为底的整数转换为以 3 为底的整数,将以 3 为底的数字相加,并将结果转换回以 10 为底

Convert base 10 integer to base 3, add base 3 number, and convert result back to base 10

我需要创建一个 Microsoft T-SQL 函数,它接受一个以 10 为底的整数(例如 20201)和第二个输入整数,将它们都转换为以 3 为底的整数,将它们相加,然后 returns 以 10 为基数的结果。

我们用它来计算学生未来的学期数。我们的术语表示为 20201 = spring、20202 = 夏季、20203 = 秋季等。我们希望将起始术语以及未来术语的数量传递给函数,然后返回结果未来的任期。

示例:

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;

这是一个 table 值函数 (tvf),它将最多预测 256 个未来的三个月。它使用数字 table(或记数 table)和序列的模数 :) 看看它是如何工作的 un-comment 组成列。

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),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
                    ,(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(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包含计算结果。