Bagaimana saya melakukan faktorial dalam SQL Server?

8

Dalam PostgreSQL, saya sering kali ingin melakukan sesuatu seperti menemukan faktorial 7. Saya dapat melakukannya dengan sangat sederhana

SELECT 7!;

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

Bahkan Excel memilikiFACT ,

=FACT(7)

Bagaimana saya melakukannya dengan SQL Server 2017 Enterprise?

Evan Carroll
sumber

Jawaban:

15

Saya tidak mengetahui fungsi bawaan untuk melakukan ini. Anda harus memutar sendiri. Inilah cara saya melakukannya:

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

Fungsi Math_Factorial adalah dalam versi gratis dari perpustakaan SQL # SQLCLR (yang saya tulis).

ATAU

jika Anda tidak membutuhkannya dalam bentuk fungsi / UDF, maka mungkin lebih efisien untuk melakukan hal berikut:

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

Kedua pendekatan yang diperlihatkan di atas memperhitungkan kondisi "khusus" untuk masuk 0sebagai "BaseNumber" dan 1alih-alih kembali 0.

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

Untuk pendekatan T-SQL, buat saja @BaseNumber = 0dan itu akan kembali 1(tidak perlu menyalin dan menempelkannya lagi di sini hanya untuk itu).

Solomon Rutzky
sumber
8

Jawaban wiki komunitas :

Anda mungkin kecewa dengan hasil dalam SQL Server dibandingkan dengan PostgreSQL (yang mampu menangani angka yang sangat besar seperti 30000! Tanpa kehilangan presisi).

Dalam SQL Server 33!adalah setinggi yang Anda bisa pergi dengan presisi yang tepat sementara 170!setinggi Anda bisa pergi sama sekali ( 171!adalah1.24E309 yang melebihi batas-batas float).

Jadi Anda bisa menghitung ulang dan menyimpannya dalam tabel dengan nilai 0 ... 170. Ini cocok pada halaman data tunggal jika kompresi digunakan.

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

Atau, berikut ini akan memberikan hasil yang akurat untuk @N hingga 10 - dan perkiraan untuk 11 + (akan lebih akurat jika berbagai fungsi / konstanta ( PI(), EXP(), POWER()) bekerja sama dengan DECIMALjenis tetapi mereka bekerja dengan FLOAThanya):

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
sumber