2009年12月30日水曜日

[他]イミディエイトウインドの強力な使い方

VisualStudioのデバッグ時に各変数の値を見る場合、

  1. マウスポインタを当てて、内容を見る
  2. ウォッチウィンドウで内容を見る
  3. ローカルウィンドウで内容を見る
  4. イミディエイトウィンドウで、?を入力した後、変数名を入力する
  5. クイックウォッチを使う

のいづれかを使用すると思います。これらはプライベート変数を見ることもできます。

しかし、メソッドを実行したい場合は、おそらくイミディエイトウィンドウを使うことになります。
で、今日気づいたのですが、このイミディエイトウィンドウはなんとプライベートメソッドを呼び出すことができるのです!!。知りませんでした。

イミディエイトウィンドウでは、インテリセンスの機能があるので、普段は入力しないのですが、たまたまインテリセンスがうまく動かなくなって、偶然コピペしたら動作したので、びっくりしちゃいました。

2009年12月28日月曜日

[.NET]メソッド名をメソッド内で得る

デバッグやトレース目的で、メソッドのIn/Outの情報をログに出したり。というのはよくある話ですが、ログに出力するロジックをコピペで作ったりしていると、よくメソッド名を間違えてしまいます。
で、先日偶然に、メソッド名を得るメソッドを発見しましたので紹介します。使用例はこんな感じ。

private void button1_Click(object sender, EventArgs e)
{
    //using System.Reflection;
    System.Diagnostics.Debug.WriteLine("メソッド = " + MethodBase.GetCurrentMethod());
    //「Void button1_Click(System.Object, System.EventArgs)」と表示される
}


でもなぜかvoidがVoidとなってしまいます。が、「わかりゃーいいんだよぉ」と考えれば十分実用的ですね。

2009年12月26日土曜日

[.NET]C#でconst配列?

C#では、定数にconstという構文があり、固定の値を定義することができます。
しかし、配列で固定の値を定義しようとすると、静的コンストラクタを実装する必要があります。
他の方法は?と思って調べると、Array.AsReadOnlyというメソッドを使うとそれらしく実現できることがわかりました。コードはこんな感じ。

public class Class1
{
    // ↓コンパイルエラー
    //const string[] arrayData = { "111", "222" };
    // 
    ReadOnlyCollection<string> arrayData = Array.AsReadOnly<string>(new string[] { "111""222" });

    public void Test()
    {

        foreach (string str in arrayData)
        {
            System.Diagnostics.Debug.WriteLine("data=" + str);
        }

        for (int i = 0; i < arrayData.Count;i++ )
        {
            string str = arrayData[i];
            System.Diagnostics.Debug.WriteLine("data=" + str);
        }

        //↓コンパイルエラー
        //arrayData[1] = "1";
    }
}

2009年12月24日木曜日

[.NET]アセンブリの差し替え

過去のシステムで何かを暗号化しているActiveXのライブラリがありました。
で、そのライブラリを.NETでInteropして使用する別のアセンブリがあり、そのアセンブリを使うために、ActiveXをregsvr32して疎通確認してみたところ、想定外のエラーが出ており、困った状態になってしまいました。

幸い、暗号化のアルゴリズムはシンプルだったので、とりあえず開発を進めるために、ActiveXのライブラリに変わる処理を.NETで組んでダミーのアセンブリを生成し、Interopアセンブリを削除し、同フォルダにダミーのを置いてみましたが、うまくいきませんでした。

そこで、以下の2つを実施すると、なんと!!動いてしまいました。


  1. AssemblyInfo.csの[assembly: Guid("xxxxxx")]をInteropのものとあわせる(Reflector for .NET で確認)
  2. アセンブリファイル名をInteropのものとあわせる


もちろん、内部のクラスのインタフェースもReflectorで確認したとおりのクラスを実装する必要があります。


何かしらの処理を一時的に差し替えたりしたい場合、便利ですねぇ。

2009年12月21日月曜日

[SQL]SQLServerのLikeは正規表現が使える!

先日、とあるシステムの調査のためSQLServerのプロファイラで発行しているSQLをトレースしていると、Like検索の対象に「ABC[0-9][0-9][0-9]」のような表現を見つけました。
はぁ?と思い、調査してみると、SQLServerのLike検索は正規表現が利用できることがわかりました。チョーびっくり。「検索条件のパターン照合」が参考になります。

このページにも書いてありますが、正規表現を使うと、インデックスが使われません。ので、データ量の多いテーブルには使わないことをお勧めします(ってオイ。これじゃあまり意味が。。。)。

2009年12月17日木曜日

[他]レガシーASP(ASP.NETではない)のデバッグテクニック(ローテク)

レガシーAPS(ASP.NETではない)のデバッグは結構面倒です。でもVisualStudioなどのツールを使うと比較的楽になります。
しかし、ツールを起動してブレークポイントをはって待っているのが困難な場合、STOPというステートメントをお勧めします。デバッグの環境つくりも含め「ASP スクリプトをデバッグする」が参考になります。

余談ですが、このSTOPステートメントはVBSなどでも利用できるようです。

2009年12月14日月曜日

[他]レガシーASP(ASP.NETではない)をデバッグする(ほぼフリー)

先日、ASPのデバッグのため環境を作っていたのですが、ライセンスの都合上VisualStudioを入れることができず、意気消沈としながらデバッグ用のログを埋め込み作業をしていました。
とはいえ、Script Debuggerを使う気にはなれず、どうしたものかと調べてみると、Microsoft Office 2003などにスクリプトデバッグするツールが同梱されていることがわかりました(Officeのインストールでオプション指定が必要。Officeツール/HTMLソース編集/Web スクリプト編集)。

設定の仕方は以下のとおり(■:チェックボックス オン □:チェックボックス オフ)。

1.IEの設定 インターネットオプション[詳細設定]
  □HTTP エラー メッセージを簡易表示する
  □スクリプトのデバッグを使用しない(Internet Explorer)
  □スクリプトのデバッグを使用しない(その他)

2.IISのサイトのプロパティ設定
 ホームディレクトリの[構成]ボタン→アプリケーションの構成の[デバッグ]タブ
 (1)デバッグのフラグ
  ■ASPのサーバー側のスクリプトのデバッグを有効にする
  □ASPのクライアント側のスクリプトのデバッグを有効にする
 (2)スクリプトのエラーメッセージ
  ●クライアントに詳細なメッセージを送る
  ○クライアントにテキストのメッセージを送る

3.デバッグ
  (1)Script Edito起動(Office XPの場合)
    C:\Program Files\Microsoft Office\Office10\MSE7.EXE
  (2)プロセスにアタッチする
    →タイトルが「Microsoft Active Server Pages・・・」
  (3)スクリプトエディタの「デバッグ」→「ウインドウ」→「実行中のドキュメント」
    ソースが表示され、ブレークポイントが設定できる

VisualStudioほど良くはありませんが、Script Debuggerに比べれば格段に使いやすいです。
ライセンスに困っている人は試してみる価値はあると思います。よ。

※:一度ブラウザでリクエストしないと、IISにキャッシュされないので、デバッグする場合は一度実行してからScript Editorを開いてください。

2009年12月8日火曜日

[他]IISのデバッグモードはシングルスレッド

先日レガシーASP(ASP.NETではない)をメンテする必要があり、調査のためデバッグ実行するためにIISの設定を変更してステップ実行していました。

しかし、特定の箇所でタイムアウトが発生していまい、どうしたものが困ってしまいました。
で、特定の箇所というのが、同じサイトのAPSからASPにMSXMLにHTTP通信している箇所でした。

いろいろ調べると、「IIS 6.0 F1: [アプリケーションの構成] - [デバッグ] タブ」にたどり着きました。そうなんです。シングルスレッドになっちゃうんです。

幸い、異なるディレクトリに配備されたASP同士でHTTP通信していたため、アプリケーションを分けて個別にデバッグ設定を行いばよい事に気づき実施してみると、うまくいきました。
片一方を非デバッグモードにすることによって、マルチスレッド動作する。という事になります。

2009年12月7日月曜日

[他]Microsoft Security Essentials

MSがフリーのアンチウイルスソフトを出しているのに今気がつきました。
Microsoft Security Essentialsというツールです。

今までAvasta!を使っていましたが、乗り換えてみることにしました。

2009年11月26日木曜日

[Silverlight]SilverlightアプリをIIS以外に配備する場合の注意事項

以前書きましたが、SilverlightアプリケーションはJavaAppletやFlashと同じようにブラウザ上でActiveXとして動作します。
そのため、IISがない環境でもよいということに成ります。がmimeの設定が必要になります。
たとえばApacheに配備する場合、設定ファイル(.htaccessもしくはhttpd.conf)に以下を加える必要があるようです。定義的にはこんなかんじ。

.xaml application/xaml+xml
.xap application/x-silverlight-app

2009年11月25日水曜日

[他]SQLServerインストール後のホスト名変更

SQLServerをインストールしたマシンのホスト名を変更すると、SQLServerが起動しなくなってしまいます。で、調べてみると、ホスト名を変更するsp_dropserverというストアドがあることがわかりました。
SQL Server 2005 スタンドアロン インスタンスをホストするコンピュータの名前を変更する方法」が参考になります。

とはいえ、SQLServerが起動しないのでストアドを実行することができないじゃん!って思ったんですが、いったん元のホスト名に戻してから実行すればよいということに気づきました。ということで事なきを得ました。

2009年11月20日金曜日

[.NET]CSVデータを操作する(OleDb)(その3 結合)

ふと思ったんですが、CSVファイル同士でのSQL的な結合が可能であれば、ファイル連携するようなバッチシステムを組む場合、DBサーバの負荷を下げたシステムを組めるのでは?と思いちょっとやってみました。
やってみたところ、あっさりできてしまいました。コードはこんな感じ。

private void button1_Click(object sender, EventArgs e)
{
    //using System.Data.Odbc; 

    string csvDir = @"d:\temp";
    string csvFileName = "Text1.csv";
    string csvFileName2 = "Text2.csv";

    //ODBC接続文字列を組み立てる 
    OdbcConnectionStringBuilder builder = new OdbcConnectionStringBuilder();
    builder["Driver"] = "Microsoft Text Driver (*.txt; *.csv)";
    builder["dbq"] = csvDir;
    builder["Extension"] = "text;HDR=No;FMT=Delimited\"";

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();

    using (OdbcConnection con = new OdbcConnection(builder.ConnectionString))
    {
        con.Open();
        string strSQL = "SELECT T1.* FROM (" + csvFileName + " T1 INNER JOIN " + csvFileName2 + " T2 ON T1.F1=T2.F1)";

        using (OdbcCommand cmd = new OdbcCommand(strSQL, con))
        using (OdbcDataReader rd = cmd.ExecuteReader())
        {
            StringBuilder sb = new StringBuilder();
            //カラム名を取得し、カンマ区切りで編集します
            for (int col = 0; col < rd.FieldCount; col++)
            {
                sb.Append(rd.GetName(col)).Append(",");
            }
            sb.Remove(sb.Length - 11);
            System.Diagnostics.Debug.WriteLine(sb);

            //データを取得しカンマ区切りで編集します
            while (rd.Read())
            {
                sb.Length = 0;
                for (int col = 0; col < rd.FieldCount; col++)
                {
                    sb.Append(rd.GetValue(col)).Append(",");
                }
                sb.Remove(sb.Length - 11);
                System.Diagnostics.Debug.WriteLine(sb);
            }
        }
    }
    sw.Stop();
    label1.Text = String.Format("完了{0:#,##0}ms", sw.ElapsedMilliseconds);
}

2009年11月18日水曜日

[Silverlight]Silverlight Tools 3 インストール失敗

詳細なメッセージは忘れてしまいましたが、プロキシ関係でID/PW照合が必要な場合、Silverlight Tools 3のインストールが失敗するようです。エラーメッセージ的には「ハッシュ値が一致しません」的なものです。

で、対処方法としては、オフラインインストール。
いろんなサイトに紹介されているようなので、詳細は割愛。


  1. Silverlight_Tools.exe /x でインストーラを展開

  2. エラーログ(インストール失敗画面の詳細リンク押下で表示される)より、ログを確認

  3. ダウンロードが失敗したファイルを手動でダウンロードし、1.と同じフォルダに格納

  4. 1.にある、インストーラっぽいものを起動

2009年11月12日木曜日

[Silverlight]VWD+Silverlight Tools 3

Silverlightの開発環境は、Visual Web Developer + Silverlight Toolsを使えば無料でそろえることができます。

で、先日までSilverlight Tools2を使っており、最近Silverlight Tools3に入れ替えました。すると、なぜかxamlのプレビュー画面が出ません。いろいろ調べると、下のペインとxamlのペインの隙間をドラッグすると表示されるようになるようです。詳しくはMSDNフォーラムのXAMLプレビュー画面で。

で、やってみましたが、正式サポートではないこともあり、私の作ったサンプルはほとんどエラー画面で終了でした。。。
画面はExpression Blendで。ってことなんでしょうか。業務アプリみたいなシンプルな画面では、あまり凝った画面はいらないんですけどねぇ。

2009年11月10日火曜日

[Silverlight]Silverlightってなんすか?(その2)

Silverlightとは私的にはクライアントGUIアプリというふうに解釈しています。
でも、JavaApplet同様に、特定のリソースにしかアクセスすることができません。SandBoxっていう仕掛けだったと思います。

JavaAppletでもそうでしたが、Silverlightから直接SQL発行はできないようです。そうなると、データのストレージは?というと、Webサービスってことになるんだろうか。。。

2009年11月5日木曜日

[Silverlight]Silverlightってなんすか?

最近調査で、Silverlightを触る機会がありました。Silverlightについては、名前と概要しか知らないところからスタートでした。で、調べてみると、私的には大まかに以下の2つと解釈しました。


  • ActiveXにて、ブラウザ上にGUIを表示する

  • Webを使ったプログラム配信(Out Of Browser)

・・・ってコレってJavaでいうところのアプレットと、今までのClickOnceの名前を変えただけでは?と思えてしまいます。。。

[ASP.NET]ログインユーザの偽装

ASP.NETでとSQLServerが別のマシンで動作している場合に、OS認証でSQLServerの接続をしたい場合、IISの実行ユーザ?で接続しにいってしまいます。
コレを別のアカウントとして偽造するために、Web.configまたはmachine.configに定義設定する必要があります。<System.Web>配下に以下の定義を追加します。これは特定のユーザーでログインしたい場合の例です。

<identity impersonate="true" userName="accountname" password="password" />


どこかのサイトで、userNameがusernameと紹介されていて、コレをコピペして「デキネーじゃん」と思っていましたが、エラーメッセージをよく見ると「大文字と小文字を区別します」旨記載がありましたので、ようやく気づきました。

2009年11月1日日曜日

[他]テーブルの種類(SQLServer)

SQLServerにはユーザ定義で利用できるテーブルっぽいものが何種類かあります。でもどれがどう。というのがよくわかりません。ということでわかる範囲列挙してみました。

  1. パーマネントテーブル : 通常?のテーブル。
  2. グローバル一時テーブル : テーブル名の先頭に「##」をつけてCREATEしたテーブル。複数のセッションで共有できる一時テーブル
  3. ローカル一時テーブル :テーブル名の先頭に「#」をつけてCREATEしたテーブル。該当のセッション内のみで使用できる一時テーブル。
  4. テーブル変数 : DECLARE xxx TABLEした変数。ローカル一時テーブルのインデックス・ユニーク制約など仕えない版?

こんなところでしょうか。3と4の使い分けがいまいちよくわかりませんが、[INF] よく寄せられる質問 - SQL Server 2000 - table 変数 が参考になりますね。

2009年10月28日水曜日

[SQL]SQLServerのDB内に存際する全テーブルの件数取得(T-SQL) (その2)

GDD Blog: [SQL]SQLServerのDB内に存際する全テーブルの件数取得(T-SQL)では真っ当?に全テーブルの件数を取する。をやってみましたが、今度、sp_MSforeachtableという便利な関数?を見つけましたので、こちらで応用をやってみました。コードはこんな感じ。

--一時変数(結果格納用)
CREATE TABLE #TEMP_TABLE(
     T_NAME varchar(128)
    ,T_CNT int
)

--sp_MSforeachtableでユーザテーブル全部をループ処理
EXEC sp_MSforeachtable  @command1 ='
    INSERT INTO #TEMP_TABLE 
    SELECT 
        ''?'' as T_NAME
       ,COUNT(*) as T_CNT 
    FROM ?
'


--実行結果を表示する
SELECT * FROM #TEMP_TABLE ORDER BY T_NAME
DROP TABLE #TEMP_TABLE


?の部分がテーブル名に置き換わるので、SELECTの項目に入れており、かつ、FROMのテーブル名になるようにしてみました。

尚、このての関数でSP_MSForEachDBというのあります。関数名からもわかるとおり、こちらはテーブルではなく全DBをループして処理するようです。

2009年10月26日月曜日

[.NET]CSVデータを操作する(OleDb)(その2)

GDD Blog: [.NET]CSVデータを操作する(OleDb)では、OleDbConnectionを使ったCSV操作を行いましたが、今度はOdbcConnectionを使ってCSVの操作を行ってみました。コードはこんな感じ。

OleDbConnectionも同じかもしれませんが、1行目のデータにコーテーションがあるか無いかで型を決めている節があります。GDD Blog: [.NET]CSVデータを操作する(OleDb)のときとは微妙にデータの出具合が違う様です。
Select時に型の情報を見てみると、int32,String,DateTimeという順で並んでいました。

細かな型指定をしたい場合は、同フォルダにSchema.iniを置くとそれを参考にするらしい。こんど少し調べてみようと思います。

private void button1_Click(object sender, EventArgs e)
{
    //using System.Data.Odbc;
    string csvDir = @"d:\temp";
    string csvFileName = "test.csv";

    //ODBC接続文字列を組み立てる
    OdbcConnectionStringBuilder builder = new OdbcConnectionStringBuilder();
    builder["Driver"] = "Microsoft Text Driver (*.txt; *.csv)";
    builder["dbq"] = csvDir;
    builder["Extension"] = "asc,csv,tab,txt";

    string strSQL = "";
    int count = 0;

    using (OdbcConnection con = new OdbcConnection(builder.ConnectionString))
    {
        con.Open();

        strSQL = "SELECT count(*) FROM " + csvFileName;
        using (OdbcCommand cmd = new OdbcCommand(strSQL, con))
        {
            //スカラー値を取得します
            object obj = cmd.ExecuteScalar();
            count = (int)obj;

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("--------------------------------------");
            sb.Append("実行結果 [").Append(obj).Append("]\r\n");
            System.Diagnostics.Debug.WriteLine(sb.ToString());

        }

        strSQL = "SELECT MAX(idx) FROM " + csvFileName;
        using (OdbcCommand cmd = new OdbcCommand(strSQL, con))
        {
            //スカラー値を取得します
            object obj = cmd.ExecuteScalar();

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("--------------------------------------");
            sb.Append("実行結果 [").Append(obj).Append("]\r\n");
            System.Diagnostics.Debug.WriteLine(sb.ToString());

        }

        strSQL = "INSERT INTO " + csvFileName + " values(" + count++ + ", 'abc', '" + DateTime.Now.ToString() + "')";
        using (OdbcCommand cmd = new OdbcCommand(strSQL, con))
        {

            //更新処理を実行します
            int result = cmd.ExecuteNonQuery();

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("--------------------------------------");
            sb.Append("登録件数 [").Append(result).Append("]\r\n");
            System.Diagnostics.Debug.WriteLine(sb.ToString());

        }

        //if (count > 0)
        //{
        //    strSQL = "DELETE " + csvFileName + " WHERE idx='" + count + "'";
        //    using (OdbcCommand cmd = new OdbcCommand(strSQL, con))
        //    {
        //        //更新処理を実行します
        //        int result = cmd.ExecuteNonQuery();

        //        StringBuilder sb = new StringBuilder();
        //        sb.AppendLine("--------------------------------------");
        //        sb.Append("更新件数 [").Append(result).Append("]\r\n");
        //        System.Diagnostics.Debug.WriteLine(sb.ToString());
        //    }
        //    //strSQL = "UPDATE " + csvFileName + " SET text1 = 'def' WHERE idx='" + count + "'";
        //    strSQL = "UPDATE " + csvFileName + " SET text1 = 'def'";
        //    using (OdbcCommand cmd = new OdbcCommand(strSQL, con))
        //    {
        //        //更新処理を実行します
        //        int result = cmd.ExecuteNonQuery();

        //        StringBuilder sb = new StringBuilder();
        //        sb.AppendLine("--------------------------------------");
        //        sb.Append("更新件数 [").Append(result).Append("]\r\n");
        //        System.Diagnostics.Debug.WriteLine(sb.ToString());
        //    }
        //}

        strSQL = "SELECT * FROM " + csvFileName; //+ " WHERE idx<3";
        using (OdbcCommand cmd = new OdbcCommand(strSQL, con))
        using (OdbcDataReader rd = cmd.ExecuteReader())
        {
            PrintData(rd);
        }

    }

}

private void PrintData(OdbcDataReader rd)
{
    StringBuilder sb = new StringBuilder();
    //カラム名を取得し、カンマ区切りで編集します
    sb.AppendLine("--------------------------------------");
    for (int cnt = 0; cnt < rd.FieldCount; cnt++)
    {
        sb.Append(rd.GetName(cnt)).Append(",");
    }
    sb.Remove(sb.Length - 11);
    sb.AppendLine("");
    sb.AppendLine("--------------------------------------");

    //データを取得しカンマ区切りで編集します
    for (int row = 0; rd.Read(); row++)
    {
        for (int col = 0; col < rd.FieldCount; col++)
        {
            sb.Append(rd.GetValue(col)).Append(",");
        }
        sb.Remove(sb.Length - 11);
        sb.AppendLine("");
    }
    sb.AppendLine("--------------------------------------");

    System.Diagnostics.Debug.WriteLine(sb.ToString());

}


■実行結果

--------------------------------------
実行結果 [6]

--------------------------------------
実行結果 [5]

--------------------------------------
登録件数 [1]

--------------------------------------
idx,text1,text2
--------------------------------------
0,abc,2009/09/06 22:44:44
1,abc,2009/09/06 22:49:58
2,abc,2009/09/06 22:49:58
3,abc,2009/09/06 22:50:00
4,abc,2009/09/06 22:50:00
5,abc,2009/09/06 22:50:01
6,abc,2009/09/06 22:50:06
--------------------------------------



■データファイル

idx,text1,text2
0,"abc",2009/09/06 22:44:44
1,"abc",2009/09/06 22:49:58
2,"abc",2009/09/06 22:49:58
3,"abc",2009/09/06 22:50:00
4,"abc",2009/09/06 22:50:00
5,"abc",2009/09/06 22:50:01
6,"abc",2009/09/06 22:50:06

2009年10月20日火曜日

[.NET]ADO.NET 2.0 基本クラスおよびファクトリによる汎用的なコーディング

ADO.NET 2.0 基本クラスおよびファクトリによる汎用的なコーディングにしたがって、ファクトリメソッドを使ったDB接続(今回もCSVファイル)をやってみました。

DbProviderFactories.GetFactoryの引数で渡す情報はmachine.configに定義されており、私の環境では、
  • Odbc Data Provider(System.Data.Odbc)
  • OleDb Data Provider(System.Data.OleDb)
  • OracleClient Data Provider(System.Data.OracleClient)
  • SqlClient Data Provider(System.Data.SqlClient)
  • Microsoft SQL Server Compact Data Provider(System.Data.SqlServerCe.3.5)

が定義されていました(括弧内がファクトリに渡す文字列)。コードはこんな感じ。

private void button1_Click(object sender, EventArgs e)
{
    //using System.Data.Common;

    string csvDir = @"d:\temp";
    string csvFileName = "test.csv";

    //接続文字列を組み立てる
    DbProviderFactory f = DbProviderFactories.GetFactory("System.Data.Odbc");
    DbConnectionStringBuilder builder = f.CreateConnectionStringBuilder();
    builder["Driver"] = "Microsoft Text Driver (*.txt; *.csv)";
    builder["dbq"] = csvDir;
    builder["Extension"] = "asc,csv,tab,txt";

    using (IDbConnection con = f.CreateConnection())
    {
        con.ConnectionString = builder.ConnectionString;
        con.Open();

        string strSQL = "SELECT * FROM " + csvFileName + " WHERE idx < 3";
        using (IDbCommand cmd = f.CreateCommand())
        {
            cmd.CommandText = strSQL;
            cmd.Connection = con;
            using (IDataReader rd = cmd.ExecuteReader())
            {
                PrintData(rd);
            }
        }

    }

}

private void PrintData(IDataReader rd)
{
    StringBuilder sb = new StringBuilder();
    //カラム名を取得し、カンマ区切りで編集します
    sb.AppendLine("--------------------------------------");
    for (int cnt = 0; cnt < rd.FieldCount; cnt++)
    {
        sb.Append(rd.GetName(cnt)).Append(",");
    }
    sb.Remove(sb.Length - 11);
    sb.AppendLine("");
    sb.AppendLine("--------------------------------------");

    //データを取得しカンマ区切りで編集します
    for (int row = 0; rd.Read(); row++)
    {
        for (int col = 0; col < rd.FieldCount; col++)
        {
            sb.Append(rd.GetValue(col)).Append(",");
        }
        sb.Remove(sb.Length - 11);
        sb.AppendLine("");
    }
    sb.AppendLine("--------------------------------------");

    System.Diagnostics.Debug.WriteLine(sb.ToString());

}
}


システム的には汎用性が高い。といえそうですが、実際にお客さんの要望として、複数のDBMSに対応した。。。というのは少ないような気がします。フレームワーク的なものであれば別ですが。

まぁとはいえ単体でユニットテストを組む場合などに応用できそうですね。

[.NET]OdbcConnectionStringBuilderを使ってみた

先日、調べ事をしていると、偶然DB接続文字列を組み立てるクラスを発見しましたので、ちょっと試しに使ってみました。たとえば、CSVファイルに接続するならこんな感じ。


//ODBC接続文字列を組み立てる
OdbcConnectionStringBuilder builder = new OdbcConnectionStringBuilder();
builder["Driver"] = "Microsoft Text Driver (*.txt; *.csv)";
builder["dbq"] = csvDir;
builder["Extension"] = "asc,csv,tab,txt";

using (OdbcConnection con = new OdbcConnection(builder.ConnectionString)){
    //SQLの実行
}


ほかにもSQLConnectionStringBuilder(SQLServer用)や抽象クラスDbConnectionStringBuilderが存在します。

2009年10月17日土曜日

[SQL]SQLServer のエラー一覧(その2)

GDD Blog: [SQL]SQLServer のエラー一覧では、SQLServer自体に登録されているメッセージを取得する方法を紹介しましたが、MSのサイトにもっといいものがありましたので紹介します。

SQL Server 2008 オンライン ブック (2009 年 5 月)システム エラー メッセージ

むしろこちらのほうが詳しい。

2009年10月14日水曜日

[.NET]CSVデータを操作する(OleDb)(その3 SQLでファイルへ出力)

[.NET]CSVデータを操作する(OleDb)(その2 ファイルへ出力)では、FileのIOでソート結果をファイルに出力してみましたが、コレをOleDBを使ったSQLで実行してみました。
コードはこんな感じ。

private void button1_Click(object sender, EventArgs e)
{
    //using System.Data.OleDb;
    string csvDir = @"d:\temp";
    string csvFileName = "KEN_ALL.CSV";
    string csvOutFileName = "KEN_ALL_OUT.CSV";

    //接続文字列    
    string conString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + csvDir +
                       ";Extended Properties=\"text;HDR=No;FMT=Delimited\"";
    //OleDBでプレースホルダを使う場合はHDRが必須らしい
    string conOutString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + csvDir +
                       ";Extended Properties=\"text;HDR=Yes;FMT=Delimited\"";

    //ファイルがあったら初期化
    using (StreamWriter w = new StreamWriter(new FileStream(csvDir + @"\" + csvOutFileName, FileMode.Create))) 
    {
        w.WriteLine("F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,F11,F12,F13,F14,F15");
    }

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();


    using (OleDbConnection con = new OleDbConnection(conString))
    using (OleDbConnection conOut = new OleDbConnection(conOutString))
    {
        con.Open();
        conOut.Open();

        // HDR=Noだと、Fxというフィールド名になるらしい。↓は3つめのフィールド名でソート
        string strSQL = "SELECT * FROM " + csvFileName + " ORDER BY F3";
        string strOutSQL = "INSERT INTO " + csvOutFileName + " VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
        using (OleDbCommand cmd = new OleDbCommand(strSQL, con))
        using (OleDbDataReader rd = cmd.ExecuteReader())
        using (OleDbCommand cmdOut = new OleDbCommand(strOutSQL, conOut))
        {
            //カラムの属性から、SQLパラメータを作成する
            for (int col = 0; col < rd.FieldCount; col++)
            {
                cmdOut.Parameters.Add(new OleDbParameter(rd.GetName(col), rd.GetFieldType(col)));
            }

            //データを取得しカンマ区切りで編集します
            while (rd.Read())
            {
                for (int col = 0; col < rd.FieldCount; col++)
                {
                    cmdOut.Parameters[col].Value = rd.GetValue(col);
                }
                cmdOut.ExecuteNonQuery();
            }

        }
    }
    sw.Stop();
    label1.Text=  String.Format("完了{0:#,##0}ms", sw.ElapsedMilliseconds);
}
}


・・・実行して1時間ほど放置してみましたが終わりませんでした。で強制終了させてみると、2万件ほどデータが出力されていました。
しかも、すべてのフィールドがテキスト形式として保存されていました。型指定で手抜きしたせいだろうか。。。

パフォーマンス的な話で言えば、OleDBのCSVに対するINSERTは実用的ではありませんね。

2009年10月11日日曜日

[.NET]CSVデータを操作する(OleDb)(その2 ソートしてファイルへ出力)

CSVファイルのある列でソートしたい。といった場合、DBにいったん保存したり、何かしらコマンドを使ったり。方法はいろいろありますが、どれもしっくりきません。以前GDD Blog: [.NET]CSVデータを操作する(OleDb)で、単純にCSVファイルのCRUDを試しましたが、SELECTするときにORDER BYつけれないかなぁということでやってみました。

適当なデータが無かったので、郵便番号データ(全国版)をつかってちょっとした実験をしてみました。
このデータは12万件ありますが、郵便番号順に並んでいないようなので、これでソートしてみました。
コードはこんな感じ。

private void button1_Click(object sender, EventArgs e)
{
    //using System.Data.OleDb;
    string csvDir = @"d:\temp";
    string csvFileName = "KEN_ALL.CSV";

    //接続文字列 
    string conString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + csvDir +
                       ";Extended Properties=\"text;HDR=No;FMT=Delimited\"";

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();
   

    using (OleDbConnection con = new OleDbConnection(conString))
    {
        con.Open();

        // HDR=Noだと、Fxというフィールド名になるらしい。↓は3つめのフィールド名でソート
        string strSQL = "SELECT * FROM " + csvFileName + " ORDER BY F3";
        using (OleDbCommand cmd = new OleDbCommand(strSQL, con))
        using (OleDbDataReader rd = cmd.ExecuteReader())
        using (StreamWriter w = new StreamWriter(new FileStream(csvDir + @"\test.txt", FileMode.Create)))
        {
            StringBuilder sb = new StringBuilder();
            //カラム名を取得し、カンマ区切りで編集します
            for (int col = 0; col < rd.FieldCount; col++)
            {
                sb.Append(rd.GetName(col)).Append(",");
            }
            sb.Remove(sb.Length - 11);
            w.WriteLine(sb);

             //データを取得しカンマ区切りで編集します
            while (rd.Read())
            {
                sb.Length = 0;
                for (int col = 0; col < rd.FieldCount; col++)
                {
                    sb.Append(rd.GetValue(col)).Append(",");
                }
                sb.Remove(sb.Length - 11);
                w.WriteLine(sb);
            }
        }
    }
    sw.Stop();
    label1.Text=  String.Format("完了{0:#,##0}ms", sw.ElapsedMilliseconds);
}


パフォーマンスを測ってみました。結果こんな感じ(5回平均)。
ソートなし:4.21秒
ソートあり:10.09秒

・・・12万件を10秒とすると、1件あたり0.08msとなります。
一般的なDBと比較するとアレですが、テキストファイルで且、ファイルへの出力も含む。と考えた場合、以外といい数字のような気がします。

ちなみに、うちのマシンは1年半ほど前に購入したC2D 2GHzのノートPCです。

2009年10月8日木曜日

[SQL]実行計画の表示(SQLServer)

SQLServerでは、SQLServer Management Studio(クエリアナライザ)でSQLのコスト評価を行うことができます。実行計画の評価はGUIを使ったマップ画面で確認することできます。

しかし、複数のSQLのコスト評価を見るには適しません。ということで、クエリの実行結果として、実行結果を取得する方法を紹介します。

--実行プランTEXT形式
SET SHOWPLAN_ALL ON


--実行プランXML形式
SET SHOWPLAN_XML ON


XML形式で出力すると、後にGUIで表示することもできます。

2009年9月28日月曜日

[.NET]初Linq(LinqToSQL)

いまさら感がありますが、、、さっきはじめてLinqを使ってみました。えぇまぁLinq to SQLです。
Express Editionを使っているので、ちょっとメンドクサイ。

前準備は以下のとおり。
  1. ローカルデータベース(sdf)を作成する
  2. コマンドsqlmetal.exeで、LinqToSQLクラスを生成する("C:\program Files\Microsoft SDKs\Windows\v6.0A\bin\sqlmetal.exe" /dbml:Database1Context.dbml /context:Database1Context Database1.sdf)
コードはこんな感じ

private void button1_Click(object sender, EventArgs e)
{
    using (Database1Context context = new Database1Context("Database1.sdf"))
    {
        context.Log = Console.Out;
        var v = from test in context.T_TEST 
                 where test.PK_NO > 0
                 select test;
        foreach (var row in v)
        { 
            System.Diagnostics.Debug.WriteLine(string.Format("key:{0} data1:{1} data2:{2} date{3}", row.PK_NO, row.DATA1, row.DATA2, row.CDATE)); 
        }
    }
}

[SQL]JOIN=INNER JOIN

先日、とあるソースを見ていると、以下のようなSQLを見つけました。

SELECT * FROM tblTEST T1
JOIN tblTEST2 T2 ON T1.p_key=T2.p_key
WHERE ....


INNERでもLEFTでもRIGHTでもないJOINって?とおもって実行してみると、結局INNER JOINと等価でした。
・・・知りませんでした。

2009年9月25日金曜日

[SQL]n番目のレコード取得

SQLでソート後、n番目(この場合2番)を取得する方法を考えてみました。
コードはこんな感じ。


-- 分析関数で取得
SELECT * FROM (
SELECT *, Row_Number() OVER(ORDER BY p_key DESC) as RNK FROM tblTEST) T1
WHERE RNK=2

--ガッツで取得
SELECT top 1 * FROM tblTEST t1
WHERE t1.p_key<(SELECT MAX(p_key) FROM tblTEST)
ORDER BY p_key DESC


とりあえず、両方とも2番目が取れるものの、ガッツで取得のほうは、可読性も悪く、TotalSubtreeCostの結果もこのようになりました。

  • 集計関数→0.003934295
  • ガッツで取得→0.00657168

集計関数。。。覚えると便利そうですね。

2009年9月21日月曜日

[.NET]文字クラス

入力のチェックに、しばしば正規表現を使うことがあります。簡単なものからそうでないものまでいろいろあります。で、.NETでは、文字クラスというものが用意されております。なんでも「入力文字列と照合する文字のセット」だそうで。たとえば、いかのようなものがあります。
  • ひらがな→IsHiragana(3040-309F)
  • カタカナ→IsKatakana(30A0-30FF)
  • 漢字→IsCJKUnifiedIdeographs(4E00-9FFF)

で、たとえば、一文字以上の漢字であれば「^\p{IsCJKUnifiedIdeographs}+$」でいけるようです。こまかな話はよくわかりませんが、Unicode 4.0 および Perl 5.6 に基づいているようです。

2009年9月17日木曜日

[.NET]特殊なフォルダを取得する

先日.NETでSendToフォルダを取得する方法を調べていたのですが、意外と簡単に取れてしまいました。コードはこんな感じ。

Environment.GetFolderPath(Environment.SpecialFolder.SendTo);


えぇまぁたったコレだけなんです。
ほかにもは?とおもって検索してみると@ITにまとまった記事を発見しました。Windowsのシステム・フォルダのパスを取得するには?

備忘録代わりにメモ。

2009年9月13日日曜日

[.NET]ショートカット(lnk)を作成する(COM参照なし)

.NET Frameworkにはショートカット(lnkファイル)を作るインタフェースは提供されていません。
しかし、VBScriptではWScript.ShellのCreateShortcutを使えば簡単にショートカットを作ることができます。
そのため、.NETアプリケーションからショートカットを作りたければ、COM Interopすればよいことになります。具体的には「参照の追加」でCOM の「 Windows Script Host Object Model」を追加し、それらしいクラスを使ってVBScriptと同じような処理をすればよいことになります。しかし、アセンブリと同じフォルダにInterop アセンブリ(DLL)をおく必要があります。

個人的に・・・ですが、InteropのDLLをみるとげんなりしてしまいます。で、ほかの方法は無いか?と考えたときに、遅延バインディングでCOMを呼び出す方法を思い出しました。
VB.NETではコンパイルオプションで、遅延バインディングが可能ですが、あえてC#。コードはこんな感じ。

/// <summary>
/// ショートカットの作成
/// </summary>
/// <remarks>WSHを使用して、ショートカット(lnkファイル)を作成します。(遅延バインディング)</remarks>
/// <param name="path">出力先のファイル名(*.lnk)</param>
/// <param name="targetPath">対象のアセンブリ(*.exe)</param>
/// <param name="description">説明</param>
private void CreateShortCut(String pathString targetPathString description)
{
    //using System.Reflection;

    // WSHオブジェクトを作成し、CreateShortcutメソッドを実行する
    Type shellType = Type.GetTypeFromProgID("WScript.Shell");
    object shell = Activator.CreateInstance(shellType);
    object shortCut = shellType.InvokeMember("CreateShortcut"BindingFlags.InvokeMethodnullshellnew object[] { path });

    Type shortcutType = shell.GetType();
    // TargetPathプロパティをセットする
    shortcutType.InvokeMember("TargetPath"BindingFlags.SetPropertynullshortCutnew object[] { targetPath });
    // Descriptionプロパティをセットする
    shortcutType.InvokeMember("Description"BindingFlags.SetPropertynullshortCutnew object[] { description });
    // Saveメソッドを実行する
    shortcutType.InvokeMember("Save"BindingFlags.InvokeMethodnullshortCutnull);

}


コンパイラーにてシンタックス的なエラーを発見することができないのでアレですが、意外と簡単?ですね。

C# 4.0 では、言語レベルで遅延バインディング(Dynamic)がサポートされるようです。そうなるとこんな書き方はしなくてもすむんですけどね。

2009年9月8日火曜日

[他]VB.NETとC#のプロジェクトを混在させる(Express Edition)

#なぜ混在かアレですが。。。
いまかかわっている仕事では、C#/VB.NETと言語が混在するプロジェクトになっています。

それを解析・再構成する。ということでまだ調査段階のため、ツール類(製品版のVisual Studio)を導入することができない状態です。
で、ソース解析といってもエディタだけではしんどいので、とりあえず、C#/VB.NETのExpress Edhitionで開けるように調整しメソッドの呼び出し関係を簡単に開く(F12)することができるようになりました。
しかし、VB⇔C#の相互関係をF12で行き来することができないのでメンドクサイですが、そこは別々に調査していました。

ある日、VWDは両方の言語が使える環境であるため、もしかしたらソリューションに全部を統合できるかも。
と思ってやってみたらできちゃいました。
ほとんどが画面を持たないライブラリアプリケーションであったこともありますが、フリーでここまでの環境が作れるとは。。。いい時代になりました。

2009年9月4日金曜日

[他]Visual C#とVisual Web Developerのキーバインド

Visual C#では[F6]キーがビルドです、そのつもりでVWD(Visual Web Developer)で[F6]キーを押下すると無反応。VWDのビルドは[CTRL+B]なんです。
Visual C#でも[CTRL+B]でビルドできますが、指が[F6]になれちゃっているんです。

キーバインドは変更できるのですが、同じ系列のツールなのになんで違うんだろう?
ふしぎですねぇ。

2009年9月1日火曜日

[SQL]数値型フィールドで最小の空き番を取得する(その2)

先日紹介しましたGDD Blog: [SQL]数値型フィールドで最小の空き番を取得するでは、NOT INとNOT EXISTSを使ったSQLを紹介しました。あと、最近のDBMSではほとんど採用されている、集計関数を使った最小値を取得する方法があります。コードはこんな感じ。

SELECT
MAX(T1.ID) + 1 AS GAP
FROM (SELECT ID, Row_Number() OVER(order by ID) AS RNK FROM testtable) t1
WHERE ID = T1.RNK


実行効率については調べていません。がNOTを使っていませんので、他の方式よりよいと思われます。

2009年8月31日月曜日

[SQL]数値型フィールドで最小の空き番を取得する

先日GDD Blog: [.NET]IDENTITY指定のフィールドはロールバックしても元に戻らない(SQLServerでIDENTITYフィールドの歯抜けについて書きましたが、今日はそれを補完するために、一番小さい空き番を取得するSQLについて書きます。
調べてみると、以下のようなSQLが紹介されていました。

SELECT
    MIN(ID + 1AS GAP
FROM testtable
WHERE (ID+1NOT IN ( SELECT ID FROM testtable);


で、NOT INを使うので、インデックスが使われないのでは?と思い、相関副問い合わせを使ったSQLに書き換えてみました。
SELECT
    MIN(ID + 1AS GAP
FROM testtable
WHERE NOT EXISTS(
    SELECT 'x'
    from testtable t0
    WHERE 
        t0.ID = testtable.ID+1 
)


実行プランを見てみると、結果両方ともインデックスが使われていないことがわかりました。しかし、インデックスなしのフィールドで同SQLを実行すると相関副問い合わせのほうが若干よい結果となりました。
相関副問い合わせであればインデックスが使われるはずなんですが。。。

2009年8月28日金曜日

[.NET]WindowsFormの画面更新系メソッド

WindowsFormで使用できる画面系コントロールの親クラスControlでは、画面再描画の機能がいくつか提供されています。今日は、それらをちょっとまとめてみました。


  • Refresh:強制的に、コントロールがクライアント領域を無効化し、直後にそのコントロール自体とその子コントロールを再描画する

  • Update:コントロールによって、クライアント領域内の無効化された領域が再描画する

  • Invalidate:コントロールの特定の領域を無効にし、そのコントロールに描画メッセージを送信する

との事とらしい。特定のコントロールだけを更新したい場合でかつ即時性が必要ない場合Invalidateで、即時性がある場合Update。親子関係まで含めて更新する場合Refreshといったところでしょうか。

2009年8月25日火曜日

[.NET]一時ファイル名を生成する

ファイル転送や、大量のデータを扱う場合に、一時ファイルに保存したい場合があります。
サーバ的な機能である場合、マルチで動作している場合、重複しないファイル名を生成する必要があります。過去の私の携わったシステムでは、現在日時をミリ秒まで使用し、重複したら連番。みたいな処理を見た記憶があります。で、いまどきは?というと、その手の機能はライブラリに当たり前のように搭載されて。。。いました。使い方も簡単で、コードはこんな感じ。

string file1 = Path.GetTempFileName();
System.Diagnostics.Debug.WriteLine(file1);
//「C:\Users\genz0\AppData\Local\Temp\tmpF27.tmp」と表示されたました。

2009年8月20日木曜日

[.NET]IDENTITY指定のフィールドはロールバックしても元に戻らない(SQLServer

SQLServerにてIDENTITY指定のフィールドがあり、INSERT後にロールバックしてもIDENTITYはカウントアップするという現象が発生しました。
何で戻らないんだ?と思って調べてみると、@@IDENTITY (Transact-SQL)に戻らない的な説明が書いてありました。

GDD Blog: [他]IDENTITYフィールドに指定の値を突っこむ(SQLServer)の方法などで、IDENTITY指定されていても連番にすることはできるのですが。。。

といってもロールバックするケースというのは稀だし、型としてはintegerなので20億位の数値は作れる。そういう意味では会員のIDなどで利用する範疇では枯渇する可能性は低いのですが、なんか空き番があるのは気持ち的に。。。
連番に空きを作りたくない。という場合は、自力でなんらか実装するのがよいって事なんでしょうかねぇ。

2009年8月15日土曜日

[SQL]TOPの件数指定

SQLServer2000までは、変数にてTOPの件数を指定できなかったのですが、2005以降は指定できるようになっていました。ってことを先日発見しました。備忘録代わりにメモ。

DECLARE @CNT int
SET @CNT = 100
SELECT top (@CNT) * from xxxx

2009年8月10日月曜日

[.NET]コントロールをビットマップにコピー

ControlにDrawToBitmapというメソッドがあります。このメソッド表示している画像をそのままbitmapに保存することができます。

で、実験してみました。コードはこんな感じ。

/// <summary>
/// ビットマップを取得する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object senderEventArgs e)
{
    int w = this.Size.Width;
    int h = this.Size.Height;
    using (Bitmap bmp = new Bitmap(wh))
    {
        this.DrawToBitmap(bmpthis.Bounds);
        Clipboard.SetImage(bmp);
    }
}


コレを応用すればWebBrowserで適当なサイトを開き画像に保存みたいなことができるじゃなぁい。と思いやってみたのですが、結果は真っ白画像。WebBrowserでは当メソッドの機能が抑止されているようです。
いろんなテクニックを駆使すれば実現できるそうなのですが。。。また今度調べてみようと思います。

2009年8月6日木曜日

[SQL]月末日を取得する(T-SQL)

以前、C#で月末日を取得する方法を紹介しましたが、今回はそれをT-SQLで書いてみました。
コードはこんな感じ。

DECLARE @YYYY char(4);
DECLARE @MM char(2);
DECLARE @RESULT DateTime;

SET @YYYY = '2008';
SET @MM = '2';

--一行で書くとこんなかんじ
--SET @RESULT = DATEADD(day,-1,DATEADD(Month,1,CONVERT(DateTime, @YYYY + '/' + @MM + '/01' ,111)));
--print '@RESULT=' + CONVERT(varchar, @RESULT, 111);

-- 指定月を日付型に
SET @RESULT = CONVERT(DateTime, @YYYY + '/' + @MM + '/01' ,111);
-- +1ヶ月
SET @RESULT = DATEADD(Month,1,@RESULT);
-- -1日
SET @RESULT = DATEADD(day,-1,@RESULT);

print '@RESULT=' + CONVERT(varchar, @RESULT, 111);

2009年8月3日月曜日

[.NET]リパースポイントの判定

先日ディレクトリを再帰的に処理して。。。みたいな処理を書いていたのですが、なぜか特定のディレクトリでスタックオーバーフローが発生していました。
調べてみると、エラーの出るフォルダは、再起するリーパースポイントを設定していることがわかりました。で、リパースポイントを判定する方法を調べてみると、ファイルのアトリビュートから判定可能ということがわかりました。コードはこんな感じ。

if ((File.GetAttributes(path) & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
{
    //リーパースポイントの場合
}else{
    //リーパースポイント以外の場合
}


ほかにもさまざまな属性が取得できます。詳しくは、FileAttributes 列挙体に一覧が。

2009年7月28日火曜日

[.NET]ADO.NETで複数の実行結果を取得する

ADO.NETでは、一回のクエリーにて、複数のSQLを送り込むことができます。
たとえば、「Select * form TBL1;Select * form TBL2」というSQLを実行すると同時に2のクエリーを実行してくれます。これはSELECTだけではなく他のUPDATEやDELETEも実行できます。SQLServerでいえば、T-SQLの実行相当を行うことができます。

複数のSQLを実行した場合、複数の結果を取得するためにどのようにすればよいかというと、SqlDataReader.NextResultを使います。
詳しい説明は、DataReader によるデータの取得が参考になると思います。

で、コードはこんな感じ。

private void button1_Click(object senderRoutedEventArgs e

    using (SqlConnection con = new SqlConnection("接続文字列")) 
    using (SqlCommand cmd = new SqlCommand("適当なT-SQL"con)) 
    { 
        con.Open(); 
        using (SqlDataReader dr = cmd.ExecuteReader())
        {
            do
            {
                //実行したSQLに対する処理
            } while (dr.NextResult());
        }
    } 

2009年7月23日木曜日

[.NET]クエリアナライザのようにエコーバック?する

SQLServerのクエリアナライザやSQL Server Management Studioのようにprint文で実行した結果ってどうやって取得するんだろうというのが気になったので調べてみました。
調べてみると、SqlConnection.InfoMessageというイベントで処理できることがわかりました。で実装してみました。コードはこんな感じ。
private void button1_Click(object senderRoutedEventArgs e)
{
    using (SqlConnection con = new SqlConnection("接続文字列"))
    using (SqlCommand cmd = new SqlCommand("print 'aaa'"con))
    {
        con.InfoMessage += new SqlInfoMessageEventHandler(con_InfoMessage);
        con.Open();
        using (SqlDataReader dr = cmd.ExecuteReader()){
        }
    }
}


void con_InfoMessage(object senderSqlInfoMessageEventArgs e)
{
    System.Diagnostics.Debug.WriteLine(e.Message);
}

2009年7月17日金曜日

[.NET]WPFアプリケーションのレイアウト

先日初めてWPFアプリケーションを作ってみました。
とりあえずいつものようにコントロールをレイアウト。。。しようと思ったんですが、Docっぽいプロパティがない!。で調べてみると、DockPanelを使ってレイアウトするららしい。というのがわかりました。

でやってみました。例を載せようと思いましたが、上のリンクに書いてあるので省略。

2009年7月15日水曜日

[.NET]constとstatic readonly

constとstatic readonly。どちらも定数定義などに利用できます。
何かが違うはず。ということで、パフォーマンスの比較をやってみました。
コードがアレなので、違いが出なかっただけかもしれませんが。ほとんど誤差レベルしか差が出ませんでした。
ちなみに、static readonlyをstaticにしましたが、あまり変化が無く、誤差レベルでした。
そういう意味で(どういう意味だ?)staticで定義しておけばアセンブリが分かれてもリビルドが不要なため、staticのほうがいいといえるのかも知れません。

static readonly string STATIC_READONLY_STR = "aaaa";
const string CONST_STR = "aaaa";

//    -----------------------------------

for (int cnt = 0cnt < maxcnt++)
{
    if (CONST_STR == "aaaa")
    {
        dummy++;
    }
    else
    {
        dummy--;
    }
}

//-----------------------------------

for (int cnt = 0cnt < maxcnt++)
{
    if (STATIC_READONLY_STR == "aaaa")
    {
        dummy++;
    }
    else
    {
        dummy--;
    }
}

2009年7月12日日曜日

[.NET]月末日を取得する

月末日は、31,30,28の場合があります。さらにうるう年の場合を考慮する必要があります。普通に考えるとメンドクサイ処理を組む必要がありイラっとくるのですが、それを簡単に算出する方法を教えてもらいましたので紹介します。コードはこんな感じ。

int yyyy = 2008;
int mm = 12;

//指定月を日付型に
DateTime dt = new DateTime(yyyymm1);
//+1ヶ月
dt = dt.AddMonths(1);
//-1日
dt = dt.AddDays(-1);

System.Diagnostics.Debug.WriteLine(string.Format("結果={0:yyyy/MM/dd}"dt));


指定月の翌月1日の前日が月末日。。。まぁいわれてみればそのまんまなんですが。。。

2009年7月8日水曜日

[SQL]行値構成子

先日、SQLでとある要件を満たすUPDATE文を作る機会がありました。
いろいろ調べてみると、UPDATEのWhere句に行値構成子を使うとよさそうということがわかりました。
コードはこんな感じ。

Select * From TBL Where (a,b)=(Select ...)


しかしSQLServerのSMSで試してみると、構文エラーが発生しうまくいきませんでした。
あきらめて、相関副問い合わせ(EXISTS)で何とかその場をしのぎました。

で、その後SQLServerでは行値構成子が使えないということがわかりました。SQL92で規定されているのに。。。

2009年7月6日月曜日

[SQL]テーブルのキーおよび制約の確認(SQLServer )

先日、各テーブルのキーおよび制約について調べる作業をしていました。
既存の資料との突合せであったため、テーブルと資料を確認しながら作業をしていました。
SQLServerなので、1つづつSQL Server Management Studioで見ていけばわかるのですが、だんだん面倒になってきました。
また、ツールなどのインストールに制限があったため、キーおよびキー項目の制約を調査するSQLを作ってみました。コードはこんな感じ。

DECLARE @TABLE_NAME varchar(128);
SET @TABLE_NAME = 'tblTEST3';

SELECT
     ST.name as TABLE_NAME
    ,SI.name as INDEX_NAME
    ,SC.name as COLMN_NAME
    ,SI.type_desc as INDEX_TYPE
    ,CASE SI.is_unique WHEN 1 THEN 'UNIQ' ELSE '' END as UNIQ
    ,CASE SC.is_nullable WHEN 1 THEN '' ELSE 'NON NULL' END as NON_NULL
    --,SI.is_unique
    --,SC.is_nullable
    --,SC.*
    --,SIC.*
FROM SYS.TABLES ST
    INNER JOIN SYS.INDEXES SI on ST.object_idSI.object_id
    INNER JOIN SYS.INDEX_COLUMNS SIC on SI.object_idSIC.object_id AND SI.index_idSIC.index_id
    INNER JOIN SYS.COLUMNS SC ON SC.object_idSI.object_id AND SC.column_id=SIC.column_id
WHERE ST.name = @TABLE_NAME

2009年6月28日日曜日

[SQL]SQLServer のエラー一覧

先日たまたま見つけたのですが、SQLServer内部にエラー用のメッセージファイルがあります。
以下のようなSQLで内容を見ることがでます。
select * from sys.messages where language_id=1041


まぁ参考資料ってことで。

2009年6月27日土曜日

[SQL]インデックスつきView(SQLServer)

先日、某掲示板に「SQL Server 2005 インデックス付きビューによるパフォーマンスの向上」というのが紹介されていました。

軽く斜め読みした感じ、以下のような特徴があることがわかりました。


  • 集計を事前に計算し、結果をインデックスとして保存することができる

  • 元となるテーブルのキー項目に変化があると、同時にビューのインデックスも更新される

  • テーブルを事前に結合し、結果のデータを保存することができる

  • 結合または集計の組み合わせを保存できる

とのこと。そして、バッチ的にデータを作成する場合は、一度ビューを削除してからインサートし、その後ビューを再構築すればよいとのこと。この場合24×7のシステムには適応できないので、辺はトレードオフだと思いますが。。。


某客先で、パフォーマンスの出ないビューがあったので、適応できそうか調べてみようと思います。

2009年6月22日月曜日

[.NET]ClickOnce以外でアップグレード

.NET Frameworkでは、ClickOnceというアプリケーション配布の形態があります。
便利な機能とは思いますが、私自身使ったことが無いというのと、資料を読む限り、少々大げさでメンドクサそうという感想があります。

で、古い記事ですが、先日偶然「クライアント・アプリケーション自動更新メカニズムの提案」というのを読みました。

.NET Framwork1.0時代の資料のため、おそらくノータッチ・デプロイメントやClickOnceのような機能が確立する前のものだと思います。

もし、ClickOnceのような枠組みを使いたくない場合は、参考になるかも。

2009年6月19日金曜日

[.NET]IPAddress取得

先日MACアドレスの取得をやったので、ついでにIPアドレスの取得をやってみました。
IPv6の情報も取れるんですねぇ。コードはこんな感じ。

private void button1_Click(object senderEventArgs e)
{
    //using System.Net;

    //ホスト名取得
    String host = Dns.GetHostName();
    // IPアドレス取得
    IPAddress[] addrs = Dns.GetHostAddresses(host);
    foreach (IPAddress address in addrs)
    {
        //構成情報出力
        System.Diagnostics.Debug.WriteLine(
            String.Format("Family={0} Adddress={1}",
                address.AddressFamily,
                address.ToString()));
    }
}


■実行結果

Family=InterNetworkV6 Adddress=::1
Family=InterNetwork Adddress=192.168.0.2

2009年6月15日月曜日

[.NET]MACアドレスなどNICの物理情報取得

先日、MSDNを眺めていたら、MACアドレスが取れそうなインタフェースがありました。
気になったのでちょっと試してみました。コードはこんな感じ。

private void button1_Click(object senderEventArgs e)
{
    //using System.Net.NetworkInformation;

    //NIC構成情報取得
    NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();

    String template = "name=[{0}] mac=[{1}] stat=[{2}] speed=[{3}mbps]";

    foreach (NetworkInterface adapter in nics)
    {
        //IPネットワーク構成情報取得
        PhysicalAddress address = adapter.GetPhysicalAddress();

        //構成情報取得
        String name = adapter.Name;
        String stat = adapter.OperationalStatus.ToString();
        double speed = adapter.Speed / 1000000;
        String mac = BitConverter.ToString(address.GetAddressBytes());
        //構成情報出力
        System.Diagnostics.Debug.WriteLine(String.Format(templatenamemacstatspeed));
    }
}


■出力結果

name=[Bluetooth ネットワーク接続] mac=[00-1E-xx-xx-xx-xx] stat=[Down] speed=[0mbps]
name=[ワイヤレス ネットワーク接続] mac=[00-1C-xx-xx-xx-xx] stat=[Up] speed=[48mbps]
name=[ローカル エリア接続] mac=[00-15-xx-xx-xx-xx] stat=[Down] speed=[100mbps]

(注)xxの部分にはMACアドレスが入ります

2009年6月11日木曜日

[他]SQLServerのサービスが起動しない。。。

先日SMSでローカルのSQLServerにログインしようとしたところ、SQLServerが見つからない旨のメッセージが表示され、ログインできなくなってしまいました。
で、調べていくとSQLServerのサービスが起動していないことがわかりました。

しかし起動しない理由がわからず、イベントログを見てみると何かしらエラーなのはわかりましたが、MSのサイトへのリンクが記載されているだけで、リンクを押下してもぴんと来るような情報は得られませんでした。

その後、サービスの設定を確認したところ、起動ユーザがビルドインのユーザではなく、ログインユーザのアカウントになっていました。
その少し前にユーザのパスワードを変更する必要があり、それが原因ということがわかりました。

しかしなぜ、インストール時にサービスの起動ユーザがビルドインアカウントにならなかったのか。。。よくわかりませんね。

2009年6月6日土曜日

[.NET]Text系コントロールのイベント発生順序

いつも、どの順でイベントが発生するかを忘れてしまうのですが、MSDN(Control.Validated イベント)にまとまった記載があったので備忘録としてメモ。抜粋は以下のとおり。

■キーボード(Tab)での遷移の場合
  1. Enter
  2. GotFocus
  3. Leave
  4. Validating
  5. Validated
  6. LostFocus

■マウス or Focusメソッド

  1. Enter
  2. GotFocus
  3. LostFocus
  4. Leave
  5. Validating
  6. Validated

・・・微妙にイベントの発生順が違う。。。

2009年6月1日月曜日

[.NET]型付DataSetの入力検証を一時的に保留する

.NET Frameworkで提供されている型付DataSetは便利なクラスです。
簡単なデータ構造を一時保存したい場合や、DBの取得結果をキャッシュしたい場合などに特に便利です。
しかし、フィールドのに制約をつけるとプロパティをセットした瞬間にエラーとなります。それをすべてのフィールドをセットした後にチェックする方式にできないかなぁと思っていたら、DataRow.BeginEditをつかえば実現できるということがわかりました。コードはこんな感じ。

TestDataSet.DataTable1DataTable dt = new TestDataSet.DataTable1DataTable();
TestDataSet.DataTable1Row row = dt.NewDataTable1Row();

//インクリメントタイプのフィールド
row.AutoIncKey = 1;
//桁数指定(Max3桁)
row.LimitData = "abc";
//NotNull
row.NotNullData = "123";

dt.Rows.Add(row);

TestDataSet.DataTable1Row row2 = dt.NewDataTable1Row();
row2.AutoIncKey = 2;
row2.LimitData = "cde";
row2.NotNullData = "456";

dt.Rows.Add(row2);
dt.AcceptChanges();

dt[1].BeginEdit();

//キー重複
dt[1].AutoIncKey = 1;
//桁数オーバー
dt[1].LimitData = "4567";
//NotNull
dt[1].NotNullData = null;

//BeginEditするとEndEditまたはAcceptChangesまでチェックが保留される
dt[1].EndEdit();
dt.AcceptChanges();