SQL Server 中的 BetaInv 函数
Posted
技术标签:
【中文标题】SQL Server 中的 BetaInv 函数【英文标题】:BetaInv function in SQL Server 【发布时间】:2011-08-23 14:41:04 【问题描述】:与问题类似:Inverted beta in mysql 我需要在 SQL Server 存储过程中使用 BetaInv 函数。
功能在这里描述:Excel's BETAINV
是否有人知道 TSQL 中有类似的东西,或者您会将其包装在 CLR .NET 管理的 SQL 用户定义函数中吗?
我真的需要在存储过程中使用它,而不是在使用存储过程检索数据后在 C# 端执行后代码,因为我应该将所有逻辑保留在 db 服务器上以便更好地重用。
我可以假设在 SQL Server 中运行的 .NET 托管 udf 的执行速度与普通的本机 TSQL 函数一样快吗?
谢谢!
【问题讨论】:
我认为你必须实现你自己的。 CLR 存储过程似乎是要走的路。 @Michael,Excel 确实支持它,我们不确定我们是否应该用 C# 编写我们自己的版本,或者购买像 Extreme Numerics 这样的数学库的许可证,我做了一些测试和 BetaDist 函数, Extreme Numerics 版本比我们手动编写的版本快约 15%,我们应该计算数千条记录...我认为无论哪种方式,我都会创建一个 CLR .NET 存储过程来执行我们的 c# 代码,最终调用 Extreme Numerics。 这听起来是个不错的计划。过去,我对 CLR 存储过程很幸运。祝你好运 【参考方案1】:我最终自己实现了整个功能,这里是源代码以防有人需要它:
public static class UDFs
private const int MAXIT = 100;
private const double EPS = 0.0000003;
private const double FPMIN = 1.0E-30;
[SqlFunction(Name = "BetaInv", DataAccess = DataAccessKind.Read)]
public static SqlDouble BetaInv(SqlDouble p, SqlDouble alpha, SqlDouble beta, SqlDouble A, SqlDouble B)
return InverseBeta(p.Value, alpha.Value, beta.Value, A.Value, B.Value);
private static double InverseBeta(double p, double alpha, double beta, double A, double B)
double x = 0;
double a = 0;
double b = 1;
double precision = Math.Pow(10, -6); // converge until there is 6 decimal places precision
while ((b - a) > precision)
x = (a + b) / 2;
if (IncompleteBetaFunction(x, alpha, beta) > p)
b = x;
else
a = x;
if ((B > 0) && (A > 0))
x = x * (B - A) + A;
return x;
private static double IncompleteBetaFunction(double x, double a, double b)
double bt = 0;
if (x <= 0.0)
return 0;
if (x >= 1)
return 1;
bt = System.Math.Exp(Gammln(a + b) - Gammln(a) - Gammln(b) + a * System.Math.Log(x) + b * System.Math.Log(1.0 - x));
if (x < ((a + 1.0) / (a + b + 2.0)))
// Use continued fraction directly.
return (bt * betacf(a, b, x) / a);
else
// Use continued fraction after making the symmetry transformation.
return (1.0 - bt * betacf(b, a, 1.0 - x) / b);
private static double betacf(double a, double b, double x)
int m, m2;
double aa, c, d, del, h, qab, qam, qap;
qab = a + b; // These q’s will be used in factors that occur in the coe.cients (6.4.6).
qap = a + 1.0;
qam = a - 1.0;
c = 1.0; // First step of Lentz’s method.
d = 1.0 - qab * x / qap;
if (System.Math.Abs(d) < FPMIN)
d = FPMIN;
d = 1.0 / d;
h = d;
for (m = 1; m <= MAXIT; ++m)
m2 = 2 * m;
aa = m * (b - m) * x / ((qam + m2) * (a + m2));
d = 1.0 + aa * d; //One step (the even one) of the recurrence.
if (System.Math.Abs(d) < FPMIN)
d = FPMIN;
c = 1.0 + aa / c;
if (System.Math.Abs(c) < FPMIN)
c = FPMIN;
d = 1.0 / d;
h *= d * c;
aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
d = 1.0 + aa * d; // Next step of the recurrence (the odd one).
if (System.Math.Abs(d) < FPMIN)
d = FPMIN;
c = 1.0 + aa / c;
if (System.Math.Abs(c) < FPMIN)
c = FPMIN;
d = 1.0 / d;
del = d * c;
h *= del;
if (System.Math.Abs(del - 1.0) < EPS)
// Are we done?
break;
if (m > MAXIT)
return 0;
else
return h;
public static double Gammln(double xx)
double x, y, tmp, ser;
double[] cof = new double[] 76.180091729471457, -86.505320329416776, 24.014098240830911, -1.231739572450155, 0.001208650973866179, -0.000005395239384953 ;
y = xx;
x = xx;
tmp = x + 5.5;
tmp -= (x + 0.5) * System.Math.Log(tmp);
ser = 1.0000000001900149;
for (int j = 0; j <= 5; ++j)
y += 1;
ser += cof[j] / y;
return -tmp + System.Math.Log(2.5066282746310007 * ser / x);
正如您在代码中看到的那样,SqlFunction 正在调用 InverseBeta 私有方法,该方法使用其他几种方法来完成这项工作。
结果与 Excel.BetaInv 相同,逗号后最多 5 或 6 位。
【讨论】:
也在这里查看我有一些关于如何提高使 conf 数组静态化的速度的建议...***.com/questions/7413891/…以上是关于SQL Server 中的 BetaInv 函数的主要内容,如果未能解决你的问题,请参考以下文章