Wie mache ich Fakultäten in SQL Server?

8

In PostgreSQL möchte ich oft so etwas wie die Fakultät 7 finden. Ich kann das sehr einfach tun

SELECT 7!;

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

Sogar Excel hatFACT ,

=FACT(7)

Wie mache ich das mit SQL Server 2017 Enterprise?

Evan Carroll
quelle

Antworten:

15

Mir ist keine eingebaute Funktion bekannt, um dies zu tun. Sie müssen Ihre eigenen rollen. So mache ich das:

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

Die Math_Factorial- Funktion befindet sich in der kostenlosen Version der SQL # SQLCLR-Bibliothek (die ich geschrieben habe).

ODER

Wenn Sie es nicht in Funktions- / UDF-Form benötigen, ist es möglicherweise effizienter, Folgendes zu tun:

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

Beide oben gezeigten Ansätze berücksichtigen die "spezielle" Bedingung, 0als "BaseNumber" zu übergeben und 1statt zurückzukehren 0.

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

Für den T-SQL-Ansatz einfach machen @BaseNumber = 0und es wird zurückkehren 1(keine Notwendigkeit, es hier nur zu kopieren und wieder einzufügen).

Solomon Rutzky
quelle
8

Antwort des Community-Wikis :

Sie sind möglicherweise enttäuscht von den Ergebnissen in SQL Server im Vergleich zu PostgreSQL (das sehr große Zahlen wie 30000! Ohne Genauigkeitsverlust verarbeiten kann).

In SQL Server 33!ist so hoch wie Sie mit einer exakten Genauigkeit gehen können , während 170!so hoch ist , wie Sie überhaupt gehen kann ( 171!ist1.24E309 das die Grenzen überschreitet float).

Sie können sie also einfach vorberechnen und in einer Tabelle mit Werten speichern 0 ... 170. Dies passt auf eine einzelne Datenseite, wenn die Komprimierung verwendet wird.

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

Alternativ wird die folgende genaue Ergebnisse für @N bis zu 10 geben - und die ungefähre für 11+ (es wäre genauer , wenn die verschiedenen Funktionen / Konstanten ( PI(), EXP(), POWER()mit gearbeitet) DECIMALTypen , aber sie arbeiten mit FLOATnur):

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))
    );
Martin Smith
quelle