如何在 SQL Server 中进行阶乘?

Eva*_*oll 8 sql-server

在 PostgreSQL 中,我经常想做一些事情,比如找到7的阶乘。我可以很简单地用

SELECT 7!;

-- PostgreSQL is so full featured
-- it even supports a prefix-factorial
SELECT !!7;
Run Code Online (Sandbox Code Playgroud)

甚至Excel 也有FACT

=FACT(7)
Run Code Online (Sandbox Code Playgroud)

如何使用 SQL Server 2017 Enterprise 做到这一点?

Sol*_*zky 15

我不知道有一个内置函数可以做到这一点。你需要自己滚动。这是我如何做到的:

SELECT SQL#.Math_Factorial(5); -- 120
Run Code Online (Sandbox Code Playgroud)

Math_Factorial功能在免费版本的SQL# SQLCLR库(我写)。

或者

如果您不需要函数/UDF 形式的它,那么执行以下操作可能更有效:

DECLARE @BaseNumber INT = 5,
        @Result BIGINT = 1;

;WITH cte AS
(
  SELECT TOP (@BaseNumber) ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS [Num]
  FROM   sys.columns
  ORDER  BY Num
)
SELECT  @Result *= [Num]
FROM    cte;

SELECT @Result;
-- 120
Run Code Online (Sandbox Code Playgroud)

上面显示的两种方法都考虑了0作为“BaseNumber”传入的“特殊”条件,它返回1而不是0.

SELECT SQL#.Math_Factorial(0); -- 1
Run Code Online (Sandbox Code Playgroud)

对于 T-SQL 方法,只需 make@BaseNumber = 0它将返回1(无需为此再次复制和粘贴它)。


小智 8

社区维基答案

与 PostgreSQL 相比,您可能会对 SQL Server 中的结果感到失望(它能够处理非常大的数字,例如30000!而不会损失精度)。

在SQL Server33!是高,你可以用精确的精度,同时走170!是高,你可以都去(171!1.24E309超过的限制float)。

所以你可以预先计算它们并将它们存储在一个带有 values 的表中0 ... 170。如果使用压缩,这适合单个数据页。

CREATE TABLE dbo.Factorials
  (
     N               TINYINT PRIMARY KEY WITH (DATA_COMPRESSION = ROW),
     FactorialExact  NUMERIC(38, 0) NULL,
     FactorialApprox FLOAT NOT NULL
  );

WITH R(N, FactorialExact, FactorialApprox)
     AS (SELECT 0,
                CAST(1 AS NUMERIC(38, 0)),
                1E0
         UNION ALL
         SELECT R.N + 1,
                CASE WHEN R.N < 33 THEN ( R.N + 1 ) * R.FactorialExact END,
                CASE WHEN R.N < 170 THEN ( R.N + 1 ) * R.FactorialApprox END
         FROM   R
         WHERE  R.N < 170)
INSERT INTO dbo.Factorials
            (N,
             FactorialExact,
             FactorialApprox)
SELECT N,
       FactorialExact,
       FactorialApprox
FROM   R
OPTION (MAXRECURSION 170);
Run Code Online (Sandbox Code Playgroud)

或者,以下将为 @N 提供最多 10 的准确结果 - 并为 11+ 提供近似值(如果各种函数/常量(PI(), EXP(), POWER())与DECIMAL类型一起工作,但它们FLOAT仅适用于类型,则会更准确):

DECLARE @N integer = 10;

SELECT
    CONVERT
    (
        DECIMAL(38,0),
        SQRT(2 * PI() * @N) * 
        POWER(@N/EXP(1), @N) * 
        EXP(1.0/12.0/@N + 1.0/360.0/POWER(@N, 3))
    );
Run Code Online (Sandbox Code Playgroud)