純粋なT-SQLの場合LOG
およびEXP
float
で操作する タイプ(8バイト)。numeric(22,6)
です 、したがって、有効数字15桁では不十分です。
POWER
numeric
を返すことができます 潜在的に高い精度で入力しますが、両方ともLOG
であるため、ほとんど役に立ちません。 およびLOG10
float
のみを返すことができます とにかく。
問題を示すために、例のタイプをnumeric(15,0)
に変更します。 POWER
を使用します EXP
の代わりに :
DECLARE @TEST TABLE
(
PAR_COLUMN INT,
PERIOD INT,
VALUE NUMERIC(15, 0)
);
INSERT INTO @TEST VALUES
(1,601,10 ),
(1,602,20 ),
(1,603,30 ),
(1,604,40 ),
(1,605,50 ),
(1,606,60 ),
(2,601,100),
(2,602,200),
(2,603,300),
(2,604,400),
(2,605,500),
(2,606,600);
SELECT *,
POWER(CAST(10 AS numeric(15,0)),
Sum(LOG10(
Abs(NULLIF(VALUE, 0))
))
OVER(PARTITION BY PAR_COLUMN ORDER BY PERIOD)) AS Mul
FROM @TEST;
結果
+------------+--------+-------+-----------------+
| PAR_COLUMN | PERIOD | VALUE | Mul |
+------------+--------+-------+-----------------+
| 1 | 601 | 10 | 10 |
| 1 | 602 | 20 | 200 |
| 1 | 603 | 30 | 6000 |
| 1 | 604 | 40 | 240000 |
| 1 | 605 | 50 | 12000000 |
| 1 | 606 | 60 | 720000000 |
| 2 | 601 | 100 | 100 |
| 2 | 602 | 200 | 20000 |
| 2 | 603 | 300 | 6000000 |
| 2 | 604 | 400 | 2400000000 |
| 2 | 605 | 500 | 1200000000000 |
| 2 | 606 | 600 | 720000000000001 |
+------------+--------+-------+-----------------+
ここでの各ステップは精度を失います。 LOGの計算は精度を失い、SUMは精度を失い、EXP/POWERは精度を失います。これらの組み込み関数では、それについて多くのことができるとは思いません。
したがって、答えは-CLRをC#で使用する decimal
>
タイプ(double
ではありません )、より高い精度(28-29有効数字)をサポートします。元のSQLタイプnumeric(22,6)
それに収まるでしょう。そして、LOG/EXP
のトリックは必要ありません。 。
おっとっと。 Productを計算するCLR集計を作成しようとしました。私のテストでは機能しますが、単純な集計としてのみ機能します。つまり、
これは機能します:
SELECT T.PAR_COLUMN, [dbo].[Product](T.VALUE) AS P
FROM @TEST AS T
GROUP BY T.PAR_COLUMN;
そしてOVER (PARTITION BY)
動作:
SELECT *,
[dbo].[Product](T.VALUE)
OVER (PARTITION BY PAR_COLUMN) AS P
FROM @TEST AS T;
ただし、OVER (PARTITION BY ... ORDER BY ...)
を使用して製品を実行する 動作しません(SQL Server 2014 Express 12.0.2000.8で確認済み):
SELECT *,
[dbo].[Product](T.VALUE)
OVER (PARTITION BY T.PAR_COLUMN ORDER BY T.PERIOD
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS CUM_MUL
FROM @TEST AS T;
検索により、この接続アイテム が見つかりました 、これは「修正されません」として閉じられ、この質問 。
C#コード:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.IO;
using System.Collections.Generic;
using System.Text;
namespace RunningProduct
{
[Serializable]
[SqlUserDefinedAggregate(
Format.UserDefined,
MaxByteSize = 17,
IsInvariantToNulls = true,
IsInvariantToDuplicates = false,
IsInvariantToOrder = true,
IsNullIfEmpty = true)]
public struct Product : IBinarySerialize
{
private bool m_bIsNull; // 1 byte storage
private decimal m_Product; // 16 bytes storage
public void Init()
{
this.m_bIsNull = true;
this.m_Product = 1;
}
public void Accumulate(
[SqlFacet(Precision = 22, Scale = 6)] SqlDecimal ParamValue)
{
if (ParamValue.IsNull) return;
this.m_bIsNull = false;
this.m_Product *= ParamValue.Value;
}
public void Merge(Product other)
{
SqlDecimal otherValue = other.Terminate();
this.Accumulate(otherValue);
}
[return: SqlFacet(Precision = 22, Scale = 6)]
public SqlDecimal Terminate()
{
if (m_bIsNull)
{
return SqlDecimal.Null;
}
else
{
return m_Product;
}
}
public void Read(BinaryReader r)
{
this.m_bIsNull = r.ReadBoolean();
this.m_Product = r.ReadDecimal();
}
public void Write(BinaryWriter w)
{
w.Write(this.m_bIsNull);
w.Write(this.m_Product);
}
}
}
CLRアセンブリをインストールします:
-- Turn advanced options on
EXEC sys.sp_configure @configname = 'show advanced options', @configvalue = 1 ;
GO
RECONFIGURE WITH OVERRIDE ;
GO
-- Enable CLR
EXEC sys.sp_configure @configname = 'clr enabled', @configvalue = 1 ;
GO
RECONFIGURE WITH OVERRIDE ;
GO
CREATE ASSEMBLY [RunningProduct]
AUTHORIZATION [dbo]
FROM 'C:\RunningProduct\RunningProduct.dll'
WITH PERMISSION_SET = SAFE;
GO
CREATE AGGREGATE [dbo].[Product](@ParamValue numeric(22,6))
RETURNS numeric(22,6)
EXTERNAL NAME [RunningProduct].[RunningProduct.Product];
GO
この
彼は別のアプローチを使用していることに注意してください。カスタムの集計を作成する代わりに 関数、ポールはテーブルを返す関数を作成します。この関数は元のデータをメモリに読み込み、必要なすべての計算を実行します。
選択したプログラミング言語を使用してクライアント側でこれらの計算を実装することにより、目的の効果を達成する方が簡単な場合があります。テーブル全体を読んで、クライアントで実行中の製品を計算するだけです。サーバーで計算された実行中の製品が、データをさらに集約するより複雑な計算の中間ステップである場合、CLR関数を作成することは理にかなっています。
頭に浮かぶもう1つのアイデア。
Log
を提供するサードパーティの.NET数学ライブラリを探す およびExp
高精度で機能します。これらのスカラーのCLRバージョンを作成します 機能。次に、EXP + LOG + SUM() Over (Order by)
を使用します。 アプローチ、ここでSUM
は組み込みのT-SQL関数であり、Over (Order by)
をサポートします。 およびExp
およびLog
float
を返さないカスタムCLR関数です 、ただし高精度のdecimal
。
高精度の計算も遅くなる可能性があることに注意してください。また、クエリでCLRスカラー関数を使用すると、処理が遅くなる可能性があります。