<?xml version="1.0" encoding="UTF-8" ?> <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>SQLServer関連</title><link>http://blogs.wankuma.com/ganfield/category/1881.aspx</link><description>SQL Server関連の話題</description><managingEditor>がんふぃーるど(ganfield@wankuma.com)</managingEditor><dc:language>ja-JP</dc:language><generator>.Text Version 0.95.2004.102</generator><item><dc:creator>がんふぃーるど(ganfield@wankuma.com)</dc:creator><title>Merge文とExecuteNonQueryとSQLServerとOracle 時々バグ？ 回避策</title><link>http://blogs.wankuma.com/ganfield/archive/2009/05/25/173574.aspx</link><pubDate>Mon, 25 May 2009 21:37:00 GMT</pubDate><guid>http://blogs.wankuma.com/ganfield/archive/2009/05/25/173574.aspx</guid><wfw:comment>http://blogs.wankuma.com/ganfield/comments/173574.aspx</wfw:comment><comments>http://blogs.wankuma.com/ganfield/archive/2009/05/25/173574.aspx#Feedback</comments><slash:comments>584</slash:comments><wfw:commentRss>http://blogs.wankuma.com/ganfield/comments/commentRss/173574.aspx</wfw:commentRss><trackback:ping>http://blogs.wankuma.com/ganfield/services/trackbacks/173574.aspx</trackback:ping><description>&lt;P&gt;とある戦国時代の武将がこう言ってました。&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;鳴かぬのなら、&lt;FONT color=#c0c0c0&gt;&lt;STRIKE&gt;殺してしまおう&lt;/STRIKE&gt;&lt;/FONT&gt;鳴かせて見せよう、とほほぎす&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;色々と引用文に間違いがありますが、&lt;A href="http://blogs.wankuma.com/ganfield/archive/2009/05/23/173492.aspx"&gt;前回&lt;/A&gt;と&lt;A href="http://blogs.wankuma.com/ganfield/archive/2009/05/23/173465.aspx"&gt;前々回&lt;/A&gt;の続きです。&lt;/P&gt;
&lt;P&gt;まず、他のバージョンの組み合わせに関してですが、前回のコメントでやじゅさんがおっしゃった通り、どうもODP.NET 10.2でも発生するみたいです。&lt;/P&gt;
&lt;P&gt;Merge文使われてないのかな～なんて思いつつも、Oracleの対応を待つのも何なので、回避策を書いておこうと思います。&lt;/P&gt;
&lt;H4&gt;回避策：大人しくSystem.Data.OracleClientを使用する&lt;/H4&gt;
&lt;P&gt;半分冗談です＾＾&lt;/P&gt;
&lt;P&gt;次が本当の回避策です。&lt;/P&gt;
&lt;H4&gt;&lt;/H4&gt;
&lt;H4&gt;本当の回避策：無名ブロックとRAISE_APPLICATION_ERRORを使用する&lt;/H4&gt;
&lt;P&gt;無名ブロックというのは、ストアドされないただのプロシージャで、BEGIN～ENDで囲まれているPL/SQLです。&lt;/P&gt;
&lt;P&gt;考え方として、次のような感じになります。&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;無名ブロック内でMerge文を発行 
&lt;LI&gt;SQL%ROWCOUNTが0であれば、楽観ロック発生としRAISE_APPLICATION_ERRORで例外を挙げる&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;最後にある「RAISE_APPLICATION_ERROR」は、開発者が任意のSQLCODE（いわゆるORA-XXXXXのXXXXX部分）と任意のメッセージを返すことができるものです。&lt;/P&gt;
&lt;P&gt;RAISE_APPLICATION_ERRORについては「&lt;A href="http://www.shift-the-oracle.com/"&gt;SHIFT the Oracle&lt;/A&gt;」内の「&lt;A href="http://www.shift-the-oracle.com/plsql/exception/raise_application_error.html"&gt;RAISE_APPLICATION_ERROR&lt;/A&gt;」に分かり易く解説されています。&lt;/P&gt;
&lt;P&gt;ということで、SQL&lt;/P&gt;
&lt;DIV style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; DISPLAY: inline; FLOAT: none; PADDING-TOP: 0px" id=scid:812469c5-0cb0-4c63-8c15-c81123a09de7:051c7108-6d92-4975-9d7a-c07d91d03f5d class=wlWriterEditableSmartContent&gt;&lt;PRE class=sql name="code"&gt;BEGIN
  --MERGE実行
  MERGE INTO MergeTable Target
  USING (SELECT :key1 Key1, :col1 Col1, :col2 Col2, :optLock OptLock
    FROM DUAL) InputSrc
  ON (Target.Key1 = InputSrc.Key1)
  WHEN MATCHED THEN
    UPDATE SET
      Col1 = InputSrc.Col1
      , Col2 = InputSrc.Col2
    WHERE Target.OptLock = InputSrc.OptLock
  WHEN NOT MATCHED THEN
    INSERT (Key1, Col1, Col2, OptLock)
      VALUES (InputSrc.Key1, InputSrc.Col1, InputSrc.Col2, systimestamp);

  --件数チェック（楽観ロックに引っかかった場合は0）
  IF SQL%ROWCOUNT = 0 THEN
    RAISE_APPLICATION_ERROR(-20801, '楽観的過ぎて更新できませんでした＞＜');
  END IF;
END;&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;18行目の-20801ですが、この部分がOracleException.Numberに入ってきます。ただし、.NET側では正の数で入ってくるので、OracleException.Numberが20801のときに、OptimisticConcurrencyExceptionにラップするなりなんなりして下さい。&lt;/P&gt;
&lt;P&gt;ちなみに、ORA-20000～ORA-20999がユーザ定義のSQLCODEとしてOracleが許可している範囲ですが、他のOracleのアプリケーションでも使用されています。実際、ORA-20000あたりがOracleTextを使用していたときに発生した記憶があります。&lt;/P&gt;
&lt;P&gt;SHIFT the Oracleの説明にもありますが、ORA-20000～ORA-20999がユーザ定義のSQLCODEとして許可されているものの、被らないという保証は一切ありません。なので、仕事で使用する場合はMessageの部分に独自のエラーコードを付与し、.NET側ではOracleException.Numberと共にOracleException.Messageも同様に調べ、適切に処理する方が良いと思います。&lt;/P&gt;
&lt;P&gt;コード&lt;/P&gt;
&lt;DIV style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; DISPLAY: inline; FLOAT: none; PADDING-TOP: 0px" id=scid:812469c5-0cb0-4c63-8c15-c81123a09de7:17495b21-3900-4a7d-b781-812beca80c27 class=wlWriterEditableSmartContent&gt;&lt;PRE class=c# name="code"&gt;const string sql
= @"BEGIN
  --MERGE実行
  MERGE INTO MergeTable Target
  USING (SELECT :key1 Key1, :col1 Col1, :col2 Col2, :optLock OptLock
    FROM DUAL) InputSrc
  ON (Target.Key1 = InputSrc.Key1)
  WHEN MATCHED THEN
    UPDATE SET
      Col1 = InputSrc.Col1
      , Col2 = InputSrc.Col2
    WHERE Target.OptLock = InputSrc.OptLock
  WHEN NOT MATCHED THEN
    INSERT (Key1, Col1, Col2, OptLock)
      VALUES (InputSrc.Key1, InputSrc.Col1, InputSrc.Col2, systimestamp);

  --件数チェック（楽観ロックに引っかかった場合は0）
  IF SQL%ROWCOUNT = 0 THEN
    RAISE_APPLICATION_ERROR(-20801, 'ERR-001:楽観的過ぎて更新できませんでした＞＜');
  END IF;
END;";

private void ExecuteMergeSql()
{
    OracleConnection conn = new OracleConnection("user id=xxx;password=yyy;data source=zzz");
    try
    {
        conn.Open();
        OracleCommand command = conn.CreateCommand();
        command.BindByName = true;
        command.CommandText = sql.Replace("\r", ""); //CRLFをLFだけに修正
        command.Parameters.Add(new OracleParameter("key1", 4));
        command.Parameters.Add(new OracleParameter("col1", "aaa"));
        command.Parameters.Add(new OracleParameter("col2", "bbb"));
        command.Parameters.Add(new OracleParameter("optLock", DateTime.Now));

        command.ExecuteNonQuery();
    }
    catch (OracleException oex)
    {
        if (oex.Number == 20801)
        {
            // ここで楽観ロックエラーを処理
        }
        else
        {
            throw;
        }
    }
    finally 
    {
        conn.Close();
    }
}&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;最後に、Oracleで無名ブロックを使用する場合の注意点ですが、環境によってBEGIN～ENDの間の&lt;A href="http://blogs.wankuma.com/ganfield/archive/2008/09/16/156709.aspx"&gt;改行コードをLFだけにしなければならない場合&lt;/A&gt;があります。（31行目の部分）&lt;/P&gt;
&lt;P&gt;これを行わないと、次のようなエラーが表示されます。&lt;/P&gt;
&lt;BLOCKQUOTE&gt;Oracle.DataAccess.Client.OracleException ORA-06550: 行1、列6:&lt;BR&gt;PLS-00103: 記号""が見つかりました。 次のうちの1つが入るとき:&lt;BR&gt;begin case declare exit for goto if loop mod null pragma&lt;BR&gt;raise return select update while with ＜an identifier＞&lt;BR&gt;＜a double-quoted delimited-identifier＞ ＜a bind variable＞ ＜＜&lt;BR&gt;close current delete fetch lock insert open rollback&lt;BR&gt;savepoint set sql execute commit forall merge pipe&lt;BR&gt;場所 Oracle.DataAccess.Client.OracleException.HandleErrorHelper(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, OpoSqlValCtx* pOpoSqlValCtx, Object src, String procedure)&lt;BR&gt;場所 Oracle.DataAccess.Client.OracleException.HandleError(Int32 errCode, OracleConnection conn, String procedure, IntPtr opsErrCtx, OpoSqlValCtx* pOpoSqlValCtx, Object src)&lt;BR&gt;場所 Oracle.DataAccess.Client.OracleCommand.ExecuteNonQuery()&lt;BR&gt;場所 WindowsFormsApplication3.Form1.button1_Click(Object sender, EventArgs e) &lt;BR&gt;場所 C:\Users\ganfield\Documents\Visual Studio 2008\Projects\WindowsFormsApplication3\WindowsFormsApplication3\Form1.cs:行 58 &lt;/BLOCKQUOTE&gt;
&lt;P&gt;英語の内容だけでは何を言っているのか分かりませんｗ&lt;/P&gt;
&lt;P&gt;検証が面倒で、何故CRが許可されたりされなかったりするのか調べていません。なので、未だに原因不明。&lt;/P&gt;
&lt;P&gt;無名ブロックを書くときだけの注意点なので、上記のエラーが発生したCRを削ればいい、ぐらいに考えても良いと思います。&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;と、以上が回避策となります。これでMerge文もOracleで楽観ロック形式の同時実行制御付きで実行することができます。&lt;/P&gt;
&lt;P&gt;ま、例外があがったりするので、DBのファクトリクラスなどを使用して抽象的に処理することはできませんので、若干とほほな感じもしますが&amp;#8230;&lt;/P&gt;&lt;/A&gt;
&lt;BLOCKQUOTE&gt;&lt;/BLOCKQUOTE&gt;&lt;img src ="http://blogs.wankuma.com/ganfield/aggbug/173574.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>がんふぃーるど(ganfield@wankuma.com)</dc:creator><title>Merge文とExecuteNonQueryとSQLServerとOracle 時々バグ？</title><link>http://blogs.wankuma.com/ganfield/archive/2009/05/23/173492.aspx</link><pubDate>Sat, 23 May 2009 19:02:00 GMT</pubDate><guid>http://blogs.wankuma.com/ganfield/archive/2009/05/23/173492.aspx</guid><wfw:comment>http://blogs.wankuma.com/ganfield/comments/173492.aspx</wfw:comment><comments>http://blogs.wankuma.com/ganfield/archive/2009/05/23/173492.aspx#Feedback</comments><slash:comments>1008</slash:comments><wfw:commentRss>http://blogs.wankuma.com/ganfield/comments/commentRss/173492.aspx</wfw:commentRss><trackback:ping>http://blogs.wankuma.com/ganfield/services/trackbacks/173492.aspx</trackback:ping><description>&lt;P&gt;&lt;A href="http://blogs.wankuma.com/ganfield/archive/2009/05/23/173465.aspx"&gt;前回のブログ&lt;/A&gt;で、やじゅさんからODP側のバグじゃないかって話があがったので、一旦動作をまとめました。今回はODP.NETだけでなく、System.Data.OracleClientも含んでいます。&lt;/P&gt;
&lt;P&gt;ちなみに、&lt;A href="http://otndnld.oracle.co.jp/document/products/oracle11g/111/doc_dvd/win.111/E05791-01/OracleCommandClass.htm#i998164"&gt;ODP.NETのマニュアル&lt;/A&gt;ではExecuteNonQueryについて、次のように述べています。&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;CODE&gt;XmlCommandType&lt;/CODE&gt;および&lt;CODE&gt;CommandText&lt;/CODE&gt;プロパティを使用してSQL文またはコマンドを実行し、影響を受ける行数を戻します&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;更新していなくても、更新していても、ExecuteNonQueryメソッドから「-1」が返ってきている時点でかなりアウトくさいです。つーかアウトですよね。影響を受けた行数が負ってどういうことじゃ！&lt;FONT color=#d3d3d3&gt;逆にこっちが影響受けた！？ｿﾝﾅﾊﾞｶﾅ&lt;/FONT&gt;&lt;/P&gt;
&lt;H4&gt;DDLとコード&lt;/H4&gt;
&lt;P&gt;1. テーブル&lt;/P&gt;
&lt;DIV style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; DISPLAY: inline; FLOAT: none; PADDING-TOP: 0px" id=scid:812469c5-0cb0-4c63-8c15-c81123a09de7:ae43ed0e-b75c-43f1-989f-80a020c95b09 class=wlWriterEditableSmartContent&gt;&lt;PRE class=sql name="code"&gt;CREATE TABLE MERGETABLE
(
  KEY1 NUMBER(6,0),
  COL1 VARCHAR2(50),
  COL2 VARCHAR2(50),
  OPTLOCK TIMESTAMP(9),
  CONSTRAINT MERGETABLE_PK PRIMARY KEY (KEY1)
)&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;2. ソースコード&lt;/P&gt;
&lt;DIV style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; DISPLAY: inline; FLOAT: none; PADDING-TOP: 0px" id=scid:812469c5-0cb0-4c63-8c15-c81123a09de7:96ebd486-2783-401f-a826-4405a8d92a4d class=wlWriterEditableSmartContent&gt;&lt;PRE class=c# name="code"&gt;// Merge文
const string sql
= @"MERGE INTO MergeTable Target
USING (SELECT :key1 Key1, :col1 Col1, :col2 Col2, :optLock OptLock
  FROM DUAL) InputSrc
ON (Target.Key1 = InputSrc.Key1)
WHEN MATCHED THEN
  UPDATE SET
    Col1 = InputSrc.Col1
    , Col2 = InputSrc.Col2
  WHERE Target.OptLock = InputSrc.OptLock
WHEN NOT MATCHED THEN
  INSERT (Key1, Col1, Col2, OptLock)
    VALUES (InputSrc.Key1, InputSrc.Col1, InputSrc.Col2, systimestamp)
";

// Merge文実行
private int ExecuteMergeSql()
{
    using (OracleConnection conn = new OracleConnection("user id=xxx;password=yyy;data source=zzz"))
    {
        conn.Open();
        OracleCommand command = conn.CreateCommand();
        command.BindByName = true;
        command.CommandText = sql;
        command.Parameters.Add(new OracleParameter("key1", 2));
        command.Parameters.Add(new OracleParameter("col1", "aaa"));
        command.Parameters.Add(new OracleParameter("col2", "bbb"));
        command.Parameters.Add(new OracleParameter("optLock", DateTime.Now));

        int result = command.ExecuteNonQuery();
        return result;
    }
}&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;上記のソースコードはODP.NETのものです。System.Data.OracleClientを使用する場合は、BindByNameの一行をコメントアウトして下さい。&lt;/P&gt;
&lt;H4&gt;&lt;/H4&gt;
&lt;P&gt;&lt;/P&gt;
&lt;H4&gt;実行結果&lt;/H4&gt;
&lt;TABLE border=1 cellSpacing=0 cellPadding=2 width=591&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=186 align=middle&gt;クライアント&lt;/TD&gt;
&lt;TD vAlign=top width=124 align=middle&gt;サーバ&lt;/TD&gt;
&lt;TD vAlign=top width=71 align=middle&gt;結果&lt;/TD&gt;
&lt;TD vAlign=top width=208 align=middle&gt;備考&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=191&gt;System.Data.SqlClient&lt;/TD&gt;
&lt;TD vAlign=top width=128&gt;SQLServer2008&lt;/TD&gt;
&lt;TD vAlign=top width=70 align=middle&gt;○&lt;/TD&gt;
&lt;TD vAlign=top width=202&gt;&amp;nbsp;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=195&gt;System.Data.OracleClient&lt;/TD&gt;
&lt;TD vAlign=top width=131&gt;OracleXE&lt;/TD&gt;
&lt;TD vAlign=top width=68 align=middle&gt;○&lt;/TD&gt;
&lt;TD vAlign=top width=198&gt;&amp;nbsp;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=198 align=middle&gt;〃&lt;/TD&gt;
&lt;TD vAlign=top width=133&gt;Oracle11g&lt;/TD&gt;
&lt;TD vAlign=top width=68 align=middle&gt;○&lt;/TD&gt;
&lt;TD vAlign=top width=196&gt;&amp;nbsp;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=200&gt;ODP.NET(2.111.6.20)&lt;/TD&gt;
&lt;TD vAlign=top width=135&gt;OracleXE&lt;/TD&gt;
&lt;TD vAlign=top width=67 align=middle&gt;&amp;#215;&lt;/TD&gt;
&lt;TD vAlign=top width=193&gt;-1が返却される。&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=201 align=middle&gt;〃&lt;/TD&gt;
&lt;TD vAlign=top width=136&gt;Oracle11g&lt;/TD&gt;
&lt;TD vAlign=top width=67 align=middle&gt;&amp;#215;&lt;/TD&gt;
&lt;TD vAlign=top width=193&gt;-1が返却される。&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;なんとSystem.Data.OracleClientは正しく動いている。というか、ODP.NETの独り負けのように見える&amp;#8230;&lt;/P&gt;
&lt;P&gt;何度かMerge文の記述方法を変えたりしましたが、全てアウト。ちょっと残念だー。&lt;/P&gt;
&lt;P&gt;ほかの環境を試せたら良かったのですが、手元の環境ではこれが精一杯でした。とりあえずOracleに一報入れておきます。&lt;/P&gt;&lt;img src ="http://blogs.wankuma.com/ganfield/aggbug/173492.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>がんふぃーるど(ganfield@wankuma.com)</dc:creator><title>Merge文とExecuteNonQueryとSQLServerとOracle</title><link>http://blogs.wankuma.com/ganfield/archive/2009/05/23/173465.aspx</link><pubDate>Sat, 23 May 2009 00:19:00 GMT</pubDate><guid>http://blogs.wankuma.com/ganfield/archive/2009/05/23/173465.aspx</guid><wfw:comment>http://blogs.wankuma.com/ganfield/comments/173465.aspx</wfw:comment><comments>http://blogs.wankuma.com/ganfield/archive/2009/05/23/173465.aspx#Feedback</comments><slash:comments>471</slash:comments><wfw:commentRss>http://blogs.wankuma.com/ganfield/comments/commentRss/173465.aspx</wfw:commentRss><trackback:ping>http://blogs.wankuma.com/ganfield/services/trackbacks/173465.aspx</trackback:ping><description>&lt;H4&gt;Merge文って？&lt;/H4&gt;
&lt;P&gt;Merge文はSQL Serverでは2008より、Oracleでは9iぐらいから実装されたSQL構文です。&lt;/P&gt;
&lt;P&gt;機能としては、Insert OR Update（存在しなければInsert,存在すればUpdate）、もしくは、Insert OR Update OR&amp;nbsp;Delete（存在しなければInsert,存在すればUpdate OR Delete）を行ってくれます。&lt;/P&gt;
&lt;P&gt;ちなみに、SQL ServerとOracleではMerge文の構文が少し違ったりします。Merge文自体SQL2003から追加された標準なのですが、Oracleは以前から実装している関係上、下位互換が必要（Update時にWhere句を付与可能とか）ですし、SQL ServerはSQL Serverで独自の拡張（WHEN MATCHED のあとにANDコンディション付与可能とか）をしています。&lt;/P&gt;
&lt;P&gt;差異があるとはいえ、ほぼ同じような構文ですし、今回はExecuteNonQueryにおける違いを説明するので、あんまそっちのほうは詳しく述べません。&lt;/P&gt;
&lt;H4&gt;ExecuteNonQueryの何が違うの？&lt;/H4&gt;
&lt;P&gt;まずは次のSQLを用意します。&lt;/P&gt;
&lt;P&gt;1. SQL Server 2008用のMerge文&lt;/P&gt;
&lt;DIV style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; DISPLAY: inline; FLOAT: none; PADDING-TOP: 0px" id=scid:812469c5-0cb0-4c63-8c15-c81123a09de7:cbfb4c10-99a3-4d21-ae73-d4a809e1623a class=wlWriterEditableSmartContent&gt;&lt;PRE class=sql name="code"&gt;MERGE INTO MergeTable as Target
USING (VALUES(@key1, @col1, @col2, @optLock))
  as InputSrc (Key1, Col1, Col2, OptLock)
ON Target.Key1 = InputSrc.Key1
WHEN MATCHED AND Target.OptLock = InputSrc.OptLock THEN
  UPDATE SET
    Col1 = InputSrc.Col1
    , Col2 = InputSrc.Col2
WHEN NOT MATCHED THEN
  INSERT (Key1, Col1, Col2)
    VALUES (InputSrc.Key1, InputSrc.Col1, InputSrc.Col2);&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;2. Oracle用のMerge文&lt;/P&gt;
&lt;DIV style="PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; DISPLAY: inline; FLOAT: none; PADDING-TOP: 0px" id=scid:812469c5-0cb0-4c63-8c15-c81123a09de7:8fab930c-9258-4725-bbe0-f1e84dbf0a9d class=wlWriterEditableSmartContent&gt;&lt;PRE class=sql name="code"&gt;MERGE INTO MergeTable Target
USING (SELECT :key1 Key1, :col1 Col1, :col2 Col2, :optLock OptLock
  FROM DUAL) InputSrc
ON (Target.Key1 = InputSrc.Key1)
WHEN MATCHED THEN
  UPDATE SET
    Col1 = InputSrc.Col1
    , Col2 = InputSrc.Col2
  WHERE Target.OptLock = InputSrc.OptLock
WHEN NOT MATCHED THEN
  INSERT (Key1, Col1, Col2, OptLock)
    VALUES (InputSrc.Key1, InputSrc.Col1, InputSrc.Col2, systimestamp);&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;詳しくは述べませんといった舌の根も乾かぬうちに脱線してしまいますが（＾＾；）、二つのSQLの大きな違いは、SQLServer2008の5行目とOracleの9行目です。これらは楽観ロックを実装している部分です。SQL2003のBNFを確認したところ、そもそも標準にON句以外で追加の条件を指定する仕組みがなく、OracleとSQLServer2008ともに追加条件に対する部分を拡張しているとも取れます。（Oracleはそもそも9iの時代からこうだったのかもしれませんが&amp;#8230;（未確認））&lt;/P&gt;
&lt;P&gt;閑話休題&lt;/P&gt;
&lt;P&gt;さて、ExecuteNonQueryを実行した結果ですが、結論だけ言ってしまうと&amp;#8230;&lt;/P&gt;
&lt;P&gt;Oracleの場合、ExecuteNonQueryを使用して流すと、メソッドの返却値として「-1」が返ってきてしまいます！（環境は&lt;STRONG&gt;OracleXE&lt;/STRONG&gt;と&lt;STRONG&gt;ODP.NET 10.2&lt;/STRONG&gt;）&lt;/P&gt;
&lt;P&gt;一方、SQL Serverの方はといえば、ちゃんと更新した行数が返ってきます。さすがMS大先生。&lt;/P&gt;
&lt;P&gt;しかし、MSしっかりしているなぁ～などと思ってMSDNのSqlCommand.ExecuteNonQueryの項目を読んでみると&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;UPDATE、INSERT、および DELETE ステートメントでの戻り値は、そのコマンドの影響を受ける行数です。他の種類のステートメントでの戻り値は、-1 です。ロールバックが発生した場合も、戻り値は -1 です。&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;文書が更新されてませんなぁｗ&lt;/P&gt;&lt;img src ="http://blogs.wankuma.com/ganfield/aggbug/173465.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>がんふぃーるど(ganfield@wankuma.com)</dc:creator><title>ウィンドウ関数（分析関数） - SQL Server 2005</title><link>http://blogs.wankuma.com/ganfield/archive/2008/07/31/151200.aspx</link><pubDate>Thu, 31 Jul 2008 00:57:00 GMT</pubDate><guid>http://blogs.wankuma.com/ganfield/archive/2008/07/31/151200.aspx</guid><wfw:comment>http://blogs.wankuma.com/ganfield/comments/151200.aspx</wfw:comment><comments>http://blogs.wankuma.com/ganfield/archive/2008/07/31/151200.aspx#Feedback</comments><slash:comments>711</slash:comments><wfw:commentRss>http://blogs.wankuma.com/ganfield/comments/commentRss/151200.aspx</wfw:commentRss><trackback:ping>http://blogs.wankuma.com/ganfield/services/trackbacks/151200.aspx</trackback:ping><description>&lt;p&gt;WNDPROCとかじゃアｒ （ｒｙ&lt;/p&gt; &lt;p&gt;ウィンドウ関数はSQL Serverでも使用できます。なんてったって標準SQLですから。&lt;/p&gt; &lt;h2&gt;ウィンドウ関数を使ってみる&lt;/h2&gt; &lt;p&gt;SQL Server 2005のAdventureWorksを使用して、ウィンドウ関数を使ってみます。やりたいことは&lt;a href="http://blogs.wankuma.com/ganfield/archive/2008/07/29/150723.aspx"&gt;Oracle版&lt;/a&gt;と同じようなことですが、部署IDみたいなものが無かったので性別でウィンドウを区切ってみます。&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Employeeテーブルの中で、性別ごとに生年月日順にランク付けする。&lt;/li&gt; &lt;li&gt;ランク付けしたら、同じ性別の中で何人中何位だったかわかるようにする。&lt;/li&gt; &lt;li&gt;取得する項目はEmployeeID, 性別, 順位, 性別ごとの人数&lt;/li&gt;&lt;/ul&gt; &lt;table cellspacing="0" cellpadding="2" width="800" border="1"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign="top" width="800"&gt; &lt;p&gt;SELECT&lt;br&gt; a.EmployeeID as 社員ID,&lt;br&gt; a.Gender as 性別,&lt;br&gt; Rank() OVER (PARTITION BY a.Gender ORDER BY a.BirthDate) as 順位,&lt;br&gt; Count(a.Gender) OVER (PARTITION BY a.Gender) as 人数&lt;br&gt;FROM&lt;br&gt; HumanResources.Employee a&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;Oracleと同じですね。実行計画は次の通り&lt;/p&gt; &lt;table cellspacing="0" cellpadding="2" width="800" border="1"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign="top" width="800"&gt; &lt;p&gt;|--Nested Loops(Inner Join)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Table Spool&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Segment&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Sequence Project(DEFINE:([Expr1002]=rank))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Segment&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Segment&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Sort(ORDER BY:([a].[Gender] ASC, [a].[BirthDate] ASC))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Clustered Index Scan(OBJECT:([AdventureWorks].[HumanResources].[Employee].[PK_...] AS [a]))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Nested Loops(Inner Join, WHERE:((1)))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Compute Scalar(DEFINE:([Expr1003]=CONVERT_IMPLICIT(int,[Expr1007],0)))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Stream Aggregate(DEFINE:([Expr1007]=Count(*)))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Table Spool&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; |--Table Spool&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;&lt;strong&gt;参考文献&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://msdn.microsoft.com/ja-jp/library/ms189461.aspx"&gt;SQL Server 2005 Books Online - OVER句&lt;/a&gt;&lt;/p&gt;&lt;img src ="http://blogs.wankuma.com/ganfield/aggbug/151200.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>がんふぃーるど(ganfield@wankuma.com)</dc:creator><title>ユーザー定義集計関数：CSV形式で文字列を集計 - SQL Server 2005</title><link>http://blogs.wankuma.com/ganfield/archive/2008/06/30/146270.aspx</link><pubDate>Mon, 30 Jun 2008 01:12:00 GMT</pubDate><guid>http://blogs.wankuma.com/ganfield/archive/2008/06/30/146270.aspx</guid><wfw:comment>http://blogs.wankuma.com/ganfield/comments/146270.aspx</wfw:comment><comments>http://blogs.wankuma.com/ganfield/archive/2008/06/30/146270.aspx#Feedback</comments><slash:comments>235</slash:comments><wfw:commentRss>http://blogs.wankuma.com/ganfield/comments/commentRss/146270.aspx</wfw:commentRss><trackback:ping>http://blogs.wankuma.com/ganfield/services/trackbacks/146270.aspx</trackback:ping><description>&lt;P&gt;&lt;A href="http://blogs.wankuma.com/ganfield/archive/2008/06/22/144865.aspx"&gt;前回&lt;/A&gt;のOracleでユーザ定義集計関数を作成したので、今度はSQL Server 2005で作ってみた。ちなみに、わんくま内のblogを検索してみたらSQL CLRについてのエントリがあまり無い。&lt;/P&gt;
&lt;P&gt;人気茄子&amp;#8230;やろうと思えばDataTableでガリガリやれないこともない&amp;#8230;&lt;/P&gt;
&lt;P&gt;&amp;#8230;&lt;/P&gt;
&lt;P&gt;まあ、これからですよ！これから！ははは&lt;/P&gt;
&lt;P&gt;気を取り直して、SQL CLRで作るユーザ定義集計関数。いってみよー&lt;/P&gt;
&lt;H2&gt;大まかな流れ&lt;/H2&gt;
&lt;P&gt;Oracleの場合と違い、SQL Serverでユーザ定義集計関数を作成する場合、アセンブリを作成する必要がのですが&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;.NETでコーディングしてアセンブリ作成（署名忘れずに※１） 
&lt;LI&gt;1で作成したクラスを紐付けたユーザー定義集計関数を定義&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;と、あまり変わらないじゃん♪　（え？アセンブリを署名するのが面倒？まあ、やらないでもいい方法があるんで♪）&lt;/P&gt;
&lt;H2&gt;ユーザー定義集計関数インターフェース&lt;/H2&gt;
&lt;P&gt;インターフェースというと語弊が若干あるのですが、要する実装する必要があるインターフェース＆定義する必要のあるメソッド及びアトリビュートです。&lt;/P&gt;
&lt;P&gt;クラスに対して必要なもの&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Microsoft.SqlServer.Server.SqlUserDefinedAggregate アトリビュート（クラス）の定義 
&lt;LI&gt;Microsoft.SqlServer.Server.IBinarySerialize インターフェースの継承&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;メンバに対して必要なもの&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;public void Init() 
&lt;LI&gt;public void Accumulate(SqlString value) 
&lt;LI&gt;public void Merge(CsvConcatenation other) 
&lt;LI&gt;public SqlString Terminate() 
&lt;LI&gt;public void Read(BinaryReader r)　※IBinarySerializeの実装 
&lt;LI&gt;public void Write(BinaryWriter w)　※IBinarySerializeの実装&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;うム。上の四つはOracleと一緒だ（笑&lt;/P&gt;
&lt;P&gt;ということで、サクサクいきましょう。&lt;/P&gt;
&lt;H2&gt;CsvConcate集計関数&lt;/H2&gt;
&lt;P&gt;こういう、関数の実装部分を定義するクラスの名前っていつも悩みますね。動詞にするかしないかってことで。今回はSQL Server 2005に登録する関数と同じ名前にしたかったので、CsvConcateにしましたが、クラス名だけならCsvConcatenationにしちゃってもいいんですよね。&lt;/P&gt;
&lt;P&gt;とまあ、クラス名の話はさておき、文字列をCSV形式で集計してくれるユーザー定義集計関数を作りたいと思います。&lt;/P&gt;
&lt;P&gt;まずはMicrosoft.SqlServer.Server.SqlUserDefinedAggregate アトリビュートの定義&lt;/P&gt;
&lt;TABLE cellSpacing=0 cellPadding=2 width=715 border=1&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=713&gt;
&lt;P&gt;[Serializable]&lt;BR&gt;[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Microsoft.SqlServer.Server.Format.UserDefined, //シリアライズ形式（ネイティブかユーザ定義）&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IsInvariantToNulls = true,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // NULLに対して不変かどうか（NULLを集計しない場合はTrue）&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IsInvariantToDuplicates = false,&amp;nbsp;&amp;nbsp;&amp;nbsp; // 重複値に対して不変かどうか（重複した値を集計しない場合はTrue）&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IsInvariantToOrder = false,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // 現在は未使用（未実装）&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; MaxByteSize = 8000)]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // MAXサイズ&lt;BR&gt;public class CsvConcate : Microsoft.SqlServer.Server.IBinarySerialize&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;定義の内容はコメントの通り。NULLや重複値については用途に合わせて使い分けて下さい。&lt;/P&gt;
&lt;P&gt;次はInit～Treminateまでのメソッドです。&lt;/P&gt;
&lt;TABLE cellSpacing=0 cellPadding=2 width=717 border=1&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=715&gt;
&lt;P&gt;/// &amp;lt;summary&amp;gt;&lt;BR&gt;/// 中間結果を保持&lt;BR&gt;/// &amp;lt;/summary&amp;gt;&lt;BR&gt;private StringBuilder intermediateResult; 
&lt;P&gt;/// &amp;lt;summary&amp;gt;&lt;BR&gt;/// 初期化&lt;BR&gt;/// &amp;lt;/summary&amp;gt;&lt;BR&gt;public void Init()&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; intermediateResult = new StringBuilder();&lt;BR&gt;} 
&lt;P&gt;/// &amp;lt;summary&amp;gt;&lt;BR&gt;/// 実際の集計処理&lt;BR&gt;/// &amp;lt;/summary&amp;gt;&lt;BR&gt;/// &amp;lt;param name="value"&amp;gt;&amp;lt;/param&amp;gt;&lt;BR&gt;public void Accumulate(SqlString value)&lt;BR&gt;{ 
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (value.IsNull)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; intermediateResult.Append(string.Empty).Append(',');&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; intermediateResult.Append(value.Value).Append(','); 
&lt;P&gt;} 
&lt;P&gt;/// &amp;lt;summary&amp;gt;&lt;BR&gt;/// 部分計算された場合のマージ&lt;BR&gt;/// &amp;lt;/summary&amp;gt;&lt;BR&gt;/// &amp;lt;param name="other"&amp;gt;&amp;lt;/param&amp;gt;&lt;BR&gt;public void Merge(CsvConcate other)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; intermediateResult.Append(other.intermediateResult);&lt;BR&gt;} 
&lt;P&gt;/// &amp;lt;summary&amp;gt;&lt;BR&gt;/// 終了処理&lt;BR&gt;/// &amp;lt;/summary&amp;gt;&lt;BR&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;BR&gt;public SqlString Terminate()&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; string output = string.Empty;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // 最後のカンマはおさらば&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (intermediateResult != null &amp;amp;&amp;amp; intermediateResult.Length &amp;gt; 0)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; output = intermediateResult.ToString(0, intermediateResult.Length - 1);&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new SqlString(output);&lt;BR&gt;}&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;そんでもってIBinarySerialize インターフェースの実装(Microsoft.SqlServer.Server.Format.Nativeの場合は必要なし？)&lt;/P&gt;
&lt;TABLE cellSpacing=0 cellPadding=2 width=710 border=1&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=708&gt;
&lt;P&gt;#region IBinarySerialize メンバ 
&lt;P&gt;void IBinarySerialize.Read(BinaryReader r)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (r == null) throw new ArgumentNullException("r");&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; intermediateResult = new StringBuilder(r.ReadString());&lt;BR&gt;} 
&lt;P&gt;void IBinarySerialize.Write(BinaryWriter w)&lt;BR&gt;{&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (w == null) throw new ArgumentNullException("w");&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; w.Write(intermediateResult.ToString());&lt;BR&gt;} 
&lt;P&gt;#endregion&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;アセンブリを署名するのを忘れずに（キーペアは「sn」などで作成しましょう）※１&lt;/P&gt;
&lt;H2&gt;&lt;/H2&gt;
&lt;H2&gt;&lt;/H2&gt;
&lt;H2&gt;&lt;/H2&gt;
&lt;H2&gt;ユーザー定義集計関数の登録&lt;/H2&gt;
&lt;P&gt;さて、署名付きのアセンブリが作成できたのなら、あとは登録するだけです。とりあえず、やることは三つ&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;アセンブリの登録 
&lt;LI&gt;ユーザー定義集計関数の定義（クラスとの紐付け）&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;登録するアセンブリの配置場所ですが、こいつは適当「UserLib」に切っておいて、そこに作成したアセンブリ「StringUtility.dll」を配置します。&lt;/P&gt;
&lt;TABLE cellSpacing=0 cellPadding=2 width=706 border=1&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=704&gt;C:\Program Files\Microsoft SQL Server\90\UserLib\StringUtility.dll&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;（別にSQL Serverと同じフォルダでなくても構いません。）&lt;/P&gt;
&lt;P&gt;それと、CLR統合機能を予め有効化する必要があるので、以下のSQLを発行(参考文献：夏椰さんのを参照）&lt;/P&gt;
&lt;TABLE cellSpacing=0 cellPadding=2 width=700 border=1&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=700&gt;sp_configure 'clr enabled', 1&lt;BR&gt;GO&lt;BR&gt;RECONFIGURE&lt;BR&gt;GO&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;あとはこいつを登録してやるだけです。以下のSQLを発行&lt;/P&gt;
&lt;TABLE cellSpacing=0 cellPadding=2 width=700 border=1&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=700&gt;
&lt;P&gt;-- アセンブリのパス&lt;BR&gt;declare @asmblyPath nvarchar(1000)&lt;BR&gt;set @asmblyPath = 'C:\Program Files\Microsoft SQL Server\90\UserLib\StringUtility.dll' 
&lt;P&gt;-- アセンブリの登録&lt;BR&gt;CREATE ASSEMBLY StringUtility FROM @asmblyPath&lt;BR&gt;WITH permission_set=Safe;&lt;BR&gt;GO 
&lt;P&gt;-- ユーザー定義集計関数の定義&lt;BR&gt;CREATE AGGREGATE Concatenate(@input nvarchar(4000))&lt;BR&gt;RETURNS nvarchar(4000)&lt;BR&gt;EXTERNAL NAME [StringUtility].[Wankuma.Ganf.SqlClr.StringUtility.CsvConcate];&lt;BR&gt;GO&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;これで、完成。では実際にNorthWindで使ってみましょう。（AdventureWorksでもいいですが、あれは結構件数が入っているので、バッファがすぐにあふry）&lt;/P&gt;
&lt;H2&gt;使い方&lt;/H2&gt;
&lt;P&gt;こちらも、Oracle同様、他の集計関数と同じ使い方です。&lt;/P&gt;
&lt;TABLE cellSpacing=0 cellPadding=2 width=700 border=1&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=700&gt;
&lt;P&gt;select country, dbo.csvconcate(employeeid) &lt;BR&gt;from employees group by country&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;H2&gt;&lt;/H2&gt;
&lt;H2&gt;参考文献&lt;/H2&gt;
&lt;P&gt;&lt;A href="http://msdn.microsoft.com/ja-jp/library/ms131051.aspx"&gt;MSDN CLR ユーザー定義集計の要件&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A title=http://technet.microsoft.com/ja-jp/library/ms161551.aspx href="http://technet.microsoft.com/ja-jp/library/ms161551.aspx"&gt;http://technet.microsoft.com/ja-jp/library/ms161551.aspx&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;ユーザー定義集計関とは違いますが、数夏椰さんのとこでいくつかSQL CLRが公開されています。&lt;/P&gt;
&lt;P&gt;&lt;A href="http://kaya.wankuma.com/box/Default.aspx"&gt;夏椰の庵 - Secluded Spot of Kaya - BOX&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;※１　アセンブリの署名を行わなくても、アセンブリの登録は可能です。その場合は、データベースのTRUSTWORTHYをONにしましょう。&lt;/P&gt;&lt;img src ="http://blogs.wankuma.com/ganfield/aggbug/146270.aspx" width = "1" height = "1" /&gt;</description></item></channel></rss>