Sql server 将数字转换为单词的递归CTE
我正在寻找一个基于(公共表表达式)解决方案的简单脚本或函数,用于在T-SQL中将整数转换为英文单词。递归CTE速度快,特别是在内联表值函数中使用时,并且易于理解和维护。此外,它应该在全范围内工作(尽管我不关心负片),即从0到264-1 Edit:例如,如果递归CTE放在标量函数中(用于说明):Sql server 将数字转换为单词的递归CTE,sql-server,tsql,text,data-conversion,Sql Server,Tsql,Text,Data Conversion,我正在寻找一个基于(公共表表达式)解决方案的简单脚本或函数,用于在T-SQL中将整数转换为英文单词。递归CTE速度快,特别是在内联表值函数中使用时,并且易于理解和维护。此外,它应该在全范围内工作(尽管我不关心负片),即从0到264-1 Edit:例如,如果递归CTE放在标量函数中(用于说明):选择dbo.NumberToWords(0)应返回字符串“零”选择dbo.NumberToWords(1234)应该返回类似于“一千二百三十四”或“一千二百三十四”的内容;依此类推,对于BIGINT范围内的
选择dbo.NumberToWords(0)
应返回字符串“零”选择dbo.NumberToWords(1234)
应该返回类似于“一千二百三十四”或“一千二百三十四”的内容;依此类推,对于BIGINT范围内的所有数字
我所看到的大多数其他(程序性)解决方案都是缓慢的、大数字崩溃的,或者对于某些输入返回完全错误的或没有结果的(例如,一百万)
我知道还有很多其他的程序性解决方案;我特别想要一个递归的CTE。据我所知,没有这样的解决方案。这将把BIGINT范围内的非负数转换为英语单词
CREATE FUNCTION dbo.tvfNumberToEnglishWords (@Number BIGINT) -- BIGINT is 64 bit signed integer
-- Converts whole numbers into English words. Returns an empty string for negative numbers.
-- Parameter @Number is a 64-bit signed integer, which allows a range of zero to 9,223,372,036,854,775,807
-- 2018-08-29 - Dave Boltman - Created
RETURNS TABLE AS
RETURN
WITH
Smalls AS (
SELECT n, Name
FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three'), (4, 'four'), (5, 'five'), (6, 'six'), (7, 'seven'),
(8, 'eight'), (9, 'nine'), (10, 'ten'), (11, 'eleven'), (12, 'twelve'), (13, 'thirteen'), (14, 'fourteen'),
(15, 'fifteen'), (16, 'sixteen'), (17, 'seventeen'), (18, 'eighteen'), (19, 'nineteen')) AS s (n, Name)
),
Decades AS (
SELECT n, Name
FROM (VALUES (2, 'twenty'), (3, 'thirty'), (4, 'forty'), (5, 'fifty'),
(6, 'sixty'), (7, 'seventy'), (8, 'eighty'), (9, 'ninety')) AS t (n, Name)
),
Groups AS (
SELECT n, Name
FROM (VALUES (0, ''), (1, ' thousand'), (2, ' million'), (3, ' billion'), (4, ' trillion'), (5, ' quadrillion'), (6, ' quintillion')
-- up to here is enough for 64 bit integers
) AS g (n, Name)
),
cte AS (
-- Level 0 : Anchor query to start the processing
SELECT
CAST (0 AS INT) AS Level,
CAST (@Number % 1000 AS BIGINT) Lowest3Digits,
cast (@Number / 1000 AS BIGINT) AS RemainingDigits,
CASE WHEN @Number = 0 THEN CAST ('zero' AS VARCHAR(1024)) ELSE '' END TextSoFar
UNION ALL
-- Recursive query based on the previous level
SELECT
Level + 1 AS Level, -- increase the level
RemainingDigits % 1000 AS Lowest3Digits,
RemainingDigits / 1000 AS RemainingDigits,
-- Busniess part is here
CAST (
CASE WHEN (RemainingDigits > 0) AND (Lowest3Digits > 0)
THEN CASE WHEN (Hundreds > 0) OR (Level > 0) THEN ', ' ELSE ' and ' END -- change ' and ' to ' ' for American English
ELSE ''
END
+ CASE WHEN Hundreds > 0 THEN (SELECT Name FROM Smalls WHERE n = Hundreds) + ' hundred' ELSE '' END
+ CASE WHEN (Hundreds > 0) AND (TensUnits > 0) THEN ' and ' ELSE '' END
+ CASE WHEN TensUnits >= 20
THEN (SELECT Name FROM Decades WHERE n = Tens)
+ CASE WHEN Units > 0 THEN (SELECT '-' + Name FROM Smalls WHERE n = Units) ELSE '' END
ELSE CASE WHEN TensUnits > 0 THEN (SELECT Name FROM Smalls WHERE n = TensUnits) ELSE '' END
END
+ CASE WHEN Lowest3Digits > 0 THEN (SELECT Name FROM Groups WHERE n = Level) ELSE '' END
+ TextSoFar
AS VARCHAR(1024)) AS TextSoFar
FROM
( SELECT Level, Lowest3Digits, RemainingDigits, TextSoFar,
Lowest3Digits / 100 AS Hundreds, Lowest3Digits % 100 AS TensUnits, (Lowest3Digits % 100) / 10 AS Tens, Lowest3Digits % 10 AS Units
FROM cte -- Dave Boltman made this code
) AS l
WHERE -- condition for exiting the recursion
(Lowest3Digits > 0) OR (RemainingDigits > 0)
)
SELECT TOP 1 TextSoFar AS EnglishText FROM cte ORDER BY Level DESC
GO
有限的测试数据
SELECT n, dbo.fnNumberToEnglishWords(n) AS AsText
FROM (VALUES (0),(-1),(1),(19),(63),(100),(101),(147),(1000),(1001),(1056),(1100),(1110),(900456),(76543),(1000000),(1000001),(1001001),(1001000),(1234567),(1234567890),(9223372036854775807)) x (n)
结果
n AsText
--------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0 zero
-1
1 one
19 nineteen
63 sixty-three
100 one hundred
101 one hundred and one
147 one hundred and forty-seven
1000 one thousand
1001 one thousand and one
1056 one thousand and fifty-six
1100 one thousand, one hundred
1110 one thousand, one hundred and ten
900456 nine hundred thousand, four hundred and fifty-six
76543 seventy-six thousand, five hundred and forty-three
1000000 one million
1000001 one million and one
1001001 one million, one thousand and one
1001000 one million, one thousand
1234567 one million, two hundred and thirty-four thousand, five hundred and sixty-seven
1234567890 one billion, two hundred and thirty-four million, five hundred and sixty-seven thousand, eight hundred and ninety
9223372036854775807 nine quintillion, two hundred and twenty-three quadrillion, three hundred and seventy-two trillion, thirty-six billion, eight hundred and fifty-four million, seven hundred and seventy-five thousand, eight hundred and seven
编辑:(移至此处)它使用当前的英式英语。如果您喜欢美式英语,只需将上面的一个字符串文本从'和'
更改为'
(代码中有一条注释说明了如何更改)。现在的英式英语,我指的是一直在美式英语中使用的(即“十亿”表示1000000000),而不是更老的长音阶(直到70年代左右才在英式英语中使用,其中“十亿”表示一百万或10000000000)
我在结果中加入了逗号,尽管我不确定这是否是标准的。如果不需要,只需删除字符串文字中的逗号。@TimBiegeleisen感谢您的评论。然而,没有什么需要修复的。我特别说过,解决方案返回英式英语,其中1001是壹仟零壹。如果你想要美式英语,我还提供了一种调整代码的方法。还有,我特别提到了整数。2.5不是整数。很公平+1,这是一个有用的帖子。我想我会提到这一点,所以至少现在没有其他人要评论了。时间戳破了吗?上面说你同时问了又回答了这个问题?@parrishharft“回答你自己的问题/分享你的知识问答式”功能完全合法啊,我只是看了一下Meta,我没意识到现在有一个
“回答你自己的问题”
框,这就是为什么。希望我的问题现在包含“足够的细节来确定一个适当的答案”,以及“确定哪一个(如果有的话)是正确的方法”。IMHO总是局限于“一个特定的问题”,从不包含“多个不同的问题”,也不需要“整本书”来回答。请重新回答这个问题,或者让我知道问题的哪一方面仍然不符合要求。