Como faço para fatoriais no SQL Server?


8

No PostgreSQL, muitas vezes eu quero fazer algo como encontrar o fatorial de 7. Eu posso fazer isso muito simplesmente com

SELECT 7!;

-- PostgreSQL is so full featured
-- it even supports a prefix-factorial
SELECT !!7;

Até o Excel temFACT ,

=FACT(7)

Como faço isso com o SQL Server 2017 Enterprise?

Respostas:


15

Não conheço uma função interna para fazer isso. Você precisa rolar o seu próprio. Aqui está como eu faço isso:

SELECT SQL#.Math_Factorial(5); -- 120

A função Math_Factorial está na versão Free da biblioteca SQL # SQLCLR (que eu escrevi).

OU

se você não precisar dele no formato function / UDF, poderá ser mais eficiente fazer o seguinte:

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

As duas abordagens mostradas acima levam em consideração a condição "especial" de passar 0como "BaseNumber" e retornar em 1vez de 0.

SELECT SQL#.Math_Factorial(0); -- 1

Para a abordagem T-SQL, basta fazer @BaseNumber = 0e ele retornará 1(não há necessidade de copiar e colar novamente aqui apenas para isso).


8

Resposta do wiki da comunidade :

Você pode se decepcionar com os resultados no SQL Server em comparação com o PostgreSQL (que é capaz de lidar com números muito grandes, como 30000! Sem perda de precisão).

Em SQL Server 33!é tão alto quanto você pode ir com precisão exata enquanto 170!é tão alto quanto você pode ir a todos ( 171!é1.24E309 o que ultrapassa os limites da float).

Então você pode pré-calculá-los e armazená-los em uma tabela com valores 0 ... 170. Isso cabe em uma única página de dados se a compactação for usada.

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);

Alternativamente, o seguinte irá dar resultados precisos para @N até 10 - e aproximado para 11+ (seria mais preciso se as várias funções / constantes ( PI(), EXP(), POWER()) trabalhou com DECIMALtipos, mas eles trabalham com FLOATapenas):

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))
    );
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.