Come si eseguono fattoriali in SQL Server?


8

In PostgreSQL, spesso voglio fare qualcosa come trovare il fattoriale di 7. Posso farlo in modo molto semplice

SELECT 7!;

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

Anche Excel haFACT ,

=FACT(7)

Come posso farlo con SQL Server 2017 Enterprise?

Risposte:


15

Non sono a conoscenza di una funzione integrata per farlo. Devi farne uno tuo. Ecco come lo faccio:

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

La funzione Math_Factorial è nella versione gratuita della libreria SQL # SQLCLR (che ho scritto).

O

se non è necessario in formato funzione / UDF, potrebbe essere più efficiente eseguire le seguenti operazioni:

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

Entrambi gli approcci mostrati sopra tengono conto della condizione "speciale" di passare 0come "BaseNumber" e restituire 1invece di 0.

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

Per l'approccio T-SQL, basta creare @BaseNumber = 0e restituirà 1(non è necessario copiarlo e incollarlo di nuovo qui solo per quello).


8

Risposta wiki della community :

Potresti essere deluso dai risultati in SQL Server rispetto a PostgreSQL (che è in grado di gestire numeri molto grandi come 30000! Senza perdita di precisione).

In SQL Server 33!è il più alto che puoi andare con la massima precisione mentre 170!è il più alto che puoi ( 171!è il1.24E309 che supera i limiti di float).

Quindi potresti semplicemente precalcolarli e archiviarli in una tabella con valori 0 ... 170. Questo si adatta a una singola pagina di dati se viene utilizzata la compressione.

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

In alternativa, il seguente darà risultati accurati per @N fino al 10 - e approssimativa per 11+ (sarebbe più esatto se le varie funzioni / costanti ( PI(), EXP(), POWER()) ha lavorato con DECIMALi tipi, ma con cui lavorano FLOATsolo):

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))
    );
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.