がんふぃーるど室長の不定期ブログ

ただいま助手と悪戦苦闘中!

目次

Blog 利用状況

ニュース


自己紹介

名前:がんふぃーるど
肩書:室長
種別:人間・男
資格一覧:
MCP 70-215 Installing, Configurating, and Administering Microsoft Windows 2000 Server
MCTS .NET Framework 2.0 - Distributed Applications
MCTS .NET Framework 2.0 - Web Applications

犬紹介


名前:なうら
肩書:助手
種別:犬・狆・メス
誕生日:2006/7/9
特技:鼻水飛ばし、甘噛、奇襲・急襲・強襲、そそう、お手、お座り、待て

記事カテゴリ

書庫

日記カテゴリ

ギャラリ

ユーザー定義集計関数:CSV形式で文字列を集計 - SQL Server 2005

前回のOracleでユーザ定義集計関数を作成したので、今度はSQL Server 2005で作ってみた。ちなみに、わんくま内のblogを検索してみたらSQL CLRについてのエントリがあまり無い。

人気茄子…やろうと思えばDataTableでガリガリやれないこともない…

まあ、これからですよ!これから!ははは

気を取り直して、SQL CLRで作るユーザ定義集計関数。いってみよー

大まかな流れ

Oracleの場合と違い、SQL Serverでユーザ定義集計関数を作成する場合、アセンブリを作成する必要がのですが

  1. .NETでコーディングしてアセンブリ作成(署名忘れずに※1)
  2. 1で作成したクラスを紐付けたユーザー定義集計関数を定義

と、あまり変わらないじゃん♪ (え?アセンブリを署名するのが面倒?まあ、やらないでもいい方法があるんで♪)

ユーザー定義集計関数インターフェース

インターフェースというと語弊が若干あるのですが、要する実装する必要があるインターフェース&定義する必要のあるメソッド及びアトリビュートです。

クラスに対して必要なもの

  • Microsoft.SqlServer.Server.SqlUserDefinedAggregate アトリビュート(クラス)の定義
  • Microsoft.SqlServer.Server.IBinarySerialize インターフェースの継承

メンバに対して必要なもの

  • public void Init()
  • public void Accumulate(SqlString value)
  • public void Merge(CsvConcatenation other)
  • public SqlString Terminate()
  • public void Read(BinaryReader r) ※IBinarySerializeの実装
  • public void Write(BinaryWriter w) ※IBinarySerializeの実装

うム。上の四つはOracleと一緒だ(笑

ということで、サクサクいきましょう。

CsvConcate集計関数

こういう、関数の実装部分を定義するクラスの名前っていつも悩みますね。動詞にするかしないかってことで。今回はSQL Server 2005に登録する関数と同じ名前にしたかったので、CsvConcateにしましたが、クラス名だけならCsvConcatenationにしちゃってもいいんですよね。

とまあ、クラス名の話はさておき、文字列をCSV形式で集計してくれるユーザー定義集計関数を作りたいと思います。

まずはMicrosoft.SqlServer.Server.SqlUserDefinedAggregate アトリビュートの定義

[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(
    Microsoft.SqlServer.Server.Format.UserDefined, //シリアライズ形式(ネイティブかユーザ定義)
    IsInvariantToNulls = true,            // NULLに対して不変かどうか(NULLを集計しない場合はTrue)
    IsInvariantToDuplicates = false,    // 重複値に対して不変かどうか(重複した値を集計しない場合はTrue)
    IsInvariantToOrder = false,            // 現在は未使用(未実装)
    MaxByteSize = 8000)]                   // MAXサイズ
public class CsvConcate : Microsoft.SqlServer.Server.IBinarySerialize

定義の内容はコメントの通り。NULLや重複値については用途に合わせて使い分けて下さい。

次はInit~Treminateまでのメソッドです。

/// <summary>
/// 中間結果を保持
/// </summary>
private StringBuilder intermediateResult;

/// <summary>
/// 初期化
/// </summary>
public void Init()
{
    intermediateResult = new StringBuilder();
}

/// <summary>
/// 実際の集計処理
/// </summary>
/// <param name="value"></param>
public void Accumulate(SqlString value)
{

    if (value.IsNull)
    {
        intermediateResult.Append(string.Empty).Append(',');
    }
    intermediateResult.Append(value.Value).Append(',');

}

/// <summary>
/// 部分計算された場合のマージ
/// </summary>
/// <param name="other"></param>
public void Merge(CsvConcate other)
{
    intermediateResult.Append(other.intermediateResult);
}

/// <summary>
/// 終了処理
/// </summary>
/// <returns></returns>
public SqlString Terminate()
{
    string output = string.Empty;
    // 最後のカンマはおさらば
    if (intermediateResult != null && intermediateResult.Length > 0)
        output = intermediateResult.ToString(0, intermediateResult.Length - 1);
    return new SqlString(output);
}

そんでもってIBinarySerialize インターフェースの実装(Microsoft.SqlServer.Server.Format.Nativeの場合は必要なし?)

#region IBinarySerialize メンバ

void IBinarySerialize.Read(BinaryReader r)
{
    if (r == null) throw new ArgumentNullException("r");
    intermediateResult = new StringBuilder(r.ReadString());
}

void IBinarySerialize.Write(BinaryWriter w)
{
    if (w == null) throw new ArgumentNullException("w");
    w.Write(intermediateResult.ToString());
}

#endregion

アセンブリを署名するのを忘れずに(キーペアは「sn」などで作成しましょう)※1

ユーザー定義集計関数の登録

さて、署名付きのアセンブリが作成できたのなら、あとは登録するだけです。とりあえず、やることは三つ

  • アセンブリの登録
  • ユーザー定義集計関数の定義(クラスとの紐付け)

登録するアセンブリの配置場所ですが、こいつは適当「UserLib」に切っておいて、そこに作成したアセンブリ「StringUtility.dll」を配置します。

C:\Program Files\Microsoft SQL Server\90\UserLib\StringUtility.dll

(別にSQL Serverと同じフォルダでなくても構いません。)

それと、CLR統合機能を予め有効化する必要があるので、以下のSQLを発行(参考文献:夏椰さんのを参照)

sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO

あとはこいつを登録してやるだけです。以下のSQLを発行

-- アセンブリのパス
declare @asmblyPath nvarchar(1000)
set @asmblyPath = 'C:\Program Files\Microsoft SQL Server\90\UserLib\StringUtility.dll'

-- アセンブリの登録
CREATE ASSEMBLY StringUtility FROM @asmblyPath
WITH permission_set=Safe;
GO

-- ユーザー定義集計関数の定義
CREATE AGGREGATE Concatenate(@input nvarchar(4000))
RETURNS nvarchar(4000)
EXTERNAL NAME [StringUtility].[Wankuma.Ganf.SqlClr.StringUtility.CsvConcate];
GO

これで、完成。では実際にNorthWindで使ってみましょう。(AdventureWorksでもいいですが、あれは結構件数が入っているので、バッファがすぐにあふry)

使い方

こちらも、Oracle同様、他の集計関数と同じ使い方です。

select country, dbo.csvconcate(employeeid)
from employees group by country

参考文献

MSDN CLR ユーザー定義集計の要件

http://technet.microsoft.com/ja-jp/library/ms161551.aspx

ユーザー定義集計関とは違いますが、数夏椰さんのとこでいくつかSQL CLRが公開されています。

夏椰の庵 - Secluded Spot of Kaya - BOX

 

※1 アセンブリの署名を行わなくても、アセンブリの登録は可能です。その場合は、データベースのTRUSTWORTHYをONにしましょう。

投稿日時 : 2008年6月30日 1:12

コメントを追加

# ユーザー定義集計関数&hellip;とは微妙に違うCSV形式で文字列を集計 - LINQ 2008/07/02 2:05 がんふぃーるど室長の不定期ブログ

ユーザー定義集計関数&hellip;とは微妙に違うCSV形式で文字列を集計 - LINQ

タイトル  
名前  
URL
コメント