Функции SQL - факториал - PullRequest
       1

Функции SQL - факториал

10 голосов
/ 17 августа 2010

Я новичок в функциях SQL. Какой лучший способ создать функцию для факториала в SQL Server - скажем, 10!

Ответы [ 10 ]

17 голосов
/ 17 августа 2010

нерекурсивный способ

;With Nums As
(
select ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS RN
FROM sys.objects
)
SELECT  POWER(10.0, SUM(LOG10(RN)))
FROM Nums
WHERE RN <= 10

и рекурсивный способ

declare @target int
set @target=10;

WITH N AS
     (SELECT 1 AS i,
           1 AS f

     UNION ALL

     SELECT i+1,
            f*(i+1)
     FROM   N
     WHERE  i < @target
     )
SELECT f FROM N
WHERE i=@target
11 голосов
/ 17 августа 2010

Вот рекурсивное решение:

CREATE FUNCTION dbo.Factorial ( @iNumber int )
RETURNS INT
AS
BEGIN
DECLARE @i  int

    IF @iNumber <= 1
        SET @i = 1
    ELSE
        SET @i = @iNumber * dbo.Factorial( @iNumber - 1 )
RETURN (@i)
END
3 голосов
/ 18 августа 2016

- итерационный метод. - Почему итеративный? Это проще и быстрее. - Для @N от 0 до 20 это дает точный результат. - 21 даст переполнение.

DECLARE @N Bigint = 20
DECLARE @F Bigint = 1
WHILE @N > 0 BEGIN
  SET @F = @f*@n
  SET @N = @N-1
END
SELECT @F AS FACTORIAL

- Измените тип данных на float, и вы можете получить факториал до 170. - 171 приведет к переполнению. - Отметьте, что результат будет точным только на ограниченном количестве позиций.

DECLARE @N FLOAT = 170
DECLARE @F FLOAT = 1
WHILE @N > 0 BEGIN
  SET @F = @f*@n
  SET @N = @N-1
END
SELECT @F AS FACTORIAL

- Бен

2 голосов
/ 09 сентября 2016

Попробуйте это

WITH MYCTE AS(
 SELECT VAL=1,NUM =6 
 UNION ALL
 SELECT VAL=VAL*NUM,NUM = (NUM -1)
 FROM MYCTE
 WHERE NUM > 1
)                  
SELECT VAL FROM MYCTE
1 голос
/ 10 апреля 2013

... для моего метода на основе множеств:

DECLARE @n int=11, @f bigint=1;

WITH 
t(n,f) AS (SELECT TOP(@n) 
         ROW_NUMBER() OVER (ORDER BY (SELECT NULL))+1,
         ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) *
        (ROW_NUMBER() OVER (ORDER BY (SELECT NULL))+1)
     FROM sys.all_columns
     UNION SELECT 1, f=CASE WHEN @n=0 THEN 0 ELSE 1 END)
SELECT @f=@f*f
FROM t
WHERE n%2=@n%2 OR f=0;


SELECT @f AS FACTORIAL;
0 голосов
/ 06 мая 2019

Другой способ:

create function Fact(@num int)
returns bigint
as
begin
declare @i int = 1

 while @num>1
 begin
  set @i = @num *  @i
  set @num=@num-1
  end

return @i
end

select dbo.Fact(5)
0 голосов
/ 23 октября 2018

Вы спросили, какой лучший способ создать функцию для факториала в SQL Server.Как всегда, это зависит от контекста.Но если вы действительно подразумеваете это в общем смысле, где производительность имеет значение, лучший способ - без сомнения, реализовать ее как пользовательскую функцию CLR.

https://docs.microsoft.com/en-us/sql/relational-databases/clr-integration-database-objects-user-defined-functions/clr-user-defined-functions?view=sql-server-2017

Конечно, вы можете реализовать саму функцию на любом языке, который вам нравится.И long / bigint на самом деле не сокращает его для факториальной функции (bigint может уместиться только до 20 !, 21! - арифметическое переполнение).

0 голосов
/ 03 января 2017

Если у вас все в порядке с аппроксимацией, используйте Аппроксимация Стирлинга .

create table #temp (value int)

insert into #temp values (5),(6),(7),(8)

select 
    value,
    sqrt(2*3.14*value)*power((value/2.718),value) --stirling's approx.
from #temp

Обратите внимание, что вам придется указывать 0, если это необходимо.

0 голосов
/ 09 сентября 2016

Вот другой метод вычисления факториального значения целого числа в SQL Server

 create function sqlFactorial (@int int)
 returns int
 begin
  declare @factorial bigint = 1
  select @factorial = @factorial * i from dbo.NumbersTable(1,@int,1)
  return @factorial
 end

Для этого решения необходимо использовать таблицу номеров SQL . Оператор Select обновляет объявленную целочисленную переменную для каждой строки в части FROM, умножая ее на упорядоченные целочисленные значения

0 голосов
/ 09 апреля 2013

Я знаю, что немного опоздал, но стоит отметить, что рекурсивный способ, который опубликовал Мартин, не работает для 0.

Это будет (извините, у меня были проблемы с публикацией кода):


declare @target int=3;

WITH N AS
(SELECT 1 AS i, 
        1 AS f
 UNION ALL
 SELECT i+1,
        f*(i+1)
 FROM N
 WHERE  i < @target),
N0 AS
(SELECT f FROM N WHERE i=@target UNION SELECT 0)
SELECT MAX(f) FROM N0

И, кстати, более быстрая версия:

declare @target int=5;

WITH N AS
(SELECT 1 AS i, 
        1 AS f
 UNION ALL
 SELECT i+1,
        f*(i+1)
 FROM N
 WHERE i < @target),
N0 AS
(SELECT f FROM N WHERE i=@target UNION SELECT f=CASE WHEN @target=0 THEN 0 END)
SELECT f FROM N0
WHERE f>=0

Это намного быстрее, потому что я теряю функцию MAX (), которая, как и топ 1,вызывает DISTINCT сортировку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...