Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 将数字转换为单词的递归CTE_Sql Server_Tsql_Text_Data Conversion - Fatal编程技术网

Sql server 将数字转换为单词的递归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范围内的

我正在寻找一个基于(公共表表达式)解决方案的简单脚本或函数,用于在T-SQL中将整数转换为英文单词。递归CTE速度快,特别是在内联表值函数中使用时,并且易于理解和维护。此外,它应该在全范围内工作(尽管我不关心负片),即从0到264-1

Edit:例如,如果递归CTE放在标量函数中(用于说明):
选择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总是局限于“一个特定的问题”,从不包含“多个不同的问题”,也不需要“整本书”来回答。请重新回答这个问题,或者让我知道问题的哪一方面仍然不符合要求。