2009年11月14日土曜日

C#の仕様書を印刷してみた

Visual Studio2005から、C#の1.2と2.0の仕様書を印刷してみた。
中閉じ、とじしろ10mmで約120枚。
印刷して、中央で切って、のり付けして、ハンドドリルで穴をあけて、針金を通して製本テープで留めてみた。
製本はできたけど、切ったときちゃんと中央で切れていなかったのか、背の部分がガッタガタ。

http://www.geocities.jp/ryou_tanoue/

のページをみると、平とじっていう方法みたい。

他に、糸かがりとじっていうのがあるので、今度C#3.0の仕様書を印刷するときにやってみよう。
多分印刷するときに、分冊指定,5枚ごとにすれば多分よさそう。
綿糸は、裁縫のときに使う糸、麻糸は、タコ糸でいいのかな?

2009年11月11日水曜日

SQL Server 2005の表データをコピペしてExcelに貼り付けるときは

SQL Server Management Studioで、オブジェクトエクスプローラから「テーブルを開く」で、表示されたテーブルデータで左上見出し行の行番号列の交差する灰色文字なしの部分をクリック→コピー→Excelで貼り付けとすると日本語が文字化けする。

SQLのselect文の結果をコピペすると文字化けしないのに。。。

で、文字化けする場合は、形式を選択して貼り付け→Unicode テキスト で貼り付ければいいらしい。

http://social.msdn.microsoft.com/Forums/ja-JP/sqlserverja/thread/7752ea7b-0e2c-4562-a823-a68dbee1e22a


別に、形式を選択して貼り付け→テキスト でもいいと思う。
そもそも、デフォルトでHTMLで貼り付けしようとしているのが間違いのような。。。

範囲指定の日時について

画面で、A時点からB時点までという絞り込みを行う際、
日付と時間を指定しますが、とある画面では、範囲指定に、時刻を省略できるものがある。
省略した場合、どのように扱えばいいんだろう?人によって範囲内外の考え方は違うんじゃないのかな?

例えば、範囲を2009/11/10 10:00~2009/11/11 11:00と指定した場合、2009/11/11 11:00:05は範囲内?範囲外?

例えば、範囲を2009/11/10 ~2009/11/11と指定した場合、2009/11/11 00:00:00は範囲内? 2009/11/11 00:00:01は? 2009/11/11 14:20は?

境界値の扱いも難しいのに、境界が幅を持っていた場合、さらにややこしくなる。
一般的な範囲の考え方ってどういう風なんだろう?

時刻切り捨ての日付比較のsql文

時刻を無視して、日付だけでシステム日付と比較するSQL

SELECT COL_1,COL_2,...
FROM TABLE_1
WHERE
CONVERT(char, TABLE_1.LastReportDateTime,111) <= CONVERT(char, DATEADD(d, -7, CURRENT_TIMESTAMP), 111)
みたいなものを昔書いた覚えがあるけど、今考えると効率面でよくなかったですね。
問題は2点。TABLE_1の全行に対して、CONVERT関数を呼び出していることと、関数を呼び出した後の比較のため、インデックスが張ってあっても、条件の絞り込みには使えないこと。

以下のように書けば、CONVERT関数を処理するのはCURRENT_TIMESTAMPの1件(SQL処理中はCURRENT_TIMESTAMPは固定値なので)でインデックスが張ってある時に範囲外のものは比較しないで対象外にしてくれそう。
SELECT COL_1,COL_2,...
FROM TABLE_1
WHERE
TABLE_1.LastReportDateTime < CONVERT(char, DATEADD(d, -6, CURRENT_TIMESTAMP), 111)

2009年11月8日日曜日

ADO.NETでのタイムアウト値のまとめ

ADO.NETでデータベースにアクセスするときこんなかき方をすると思います。

using (TransactionScope ts = new TransactionScope())
{
DataSet1.ProjectDataTable dt1 = new DataSet1.ProjectDataTable();
using (ProjectTableAdapter ta1 = new ProjectTableAdapter())
{
dt1 = ta1.GetData();
}
}
上のプログラムで、SQL Server に接続してデータを取るのですが、このときのタイムアウトは3種類あります。
1.トランザクション処理のトランザクションタイムアウト(デフォルトでは 60 秒) 参考:http://msdn.microsoft.com/ja-jp/library/ms973865.aspx

2.SQL Serverへ接続したときの応答待ちのコネクションタイムアウト(デフォルトで15秒)参考:http://msdn.microsoft.com/ja-jp/library/system.data.sqlclient.sqlconnection.connectiontimeout(VS.80).aspx


3.SQLコマンドを実行したときの応答待ちのコマンドタイムアウト(デフォルト30秒)参考:
http://msdn.microsoft.com/ja-jp/library/system.data.sqlclient.sqlcommand.commandtimeout(VS.80).aspx

上のテーブルアダプタ操作でそれぞれのタイムアウト値を変更するのは以下の通り。


1.トランザクションタイムアウト変更

using (TransactionScope ts = new TransactionScop(TransactionScopeOption.Required,new TimeSpan(0,0,30))

2.コネクションタイムアウト変更
ta1.Connection.ConnectionTimeOut =10;
dt1 = ta1.GetData();
※TableAdapter.Connectionプロパティのスコープはinternalなので同一アセンブリからしか直接参照できない

3.コマンドタイムアウト変更
どうやら、コードを書かないと無理みたい。
http://gushwell.ldblog.jp/archives/50527778.html
で、CommandTimeOutのプロパティを外だしにして、

ta1.CommandTimeOut=60;
dt1 = ta1.GetData();

分散トランザクションへの昇格の防止について

ADO.NET2.0では、分散トランザクションへの昇格が自動で行われますが、Windows 2003やXPで開発していて、APサーバとDBサーバが分離されている場合、意図しなかったところで分散トランザクションに昇格して面倒になることがある。

例えば、以下のようなコードを書いたとき、MSDTCを設定していないとエラーとなってしまう。
using (TransactionScope ts = new TransactionScope())
{
DataSet1.ProjectDataTable dt = new DataSet1.ProjectDataTable();
using (ProjectTableAdapter ta = new ProjectTableAdapter())
{
dt = ta.GetData();
dt = ta.GetData();
}
}

エラーとなるのは以下の理由。
1.トランザクションを設定する。
2. 1個目のta.GetData()で、Connection.Open()が呼び出される。
3.GetData()用のSQLが実行される。
4.Connection.Close()が呼び出される。
5.2個目のta.GetData()で、Connection.Open()が呼び出される。
6.トランザクション中にConnection.Open()が2回目に呼び出されたので、トランザクションが分散トランザクション制御になる。
7.DBサーバ側からAPサーバ側に分散トランザクション用のやり取りが始まる。
8.MSDTCの設定で、受信を拒否していたりMSDTCが起動していなかったりすると、分散トランザクション制御の処理が出来ない。
9.トランザクション処理がエラーとなる。

なので、APサーバ側でもMSDTCの受信を許可するようにするか、分散トランザクションへの昇格を防止すればよいことになる。

そもそも、何で、同じテーブルアダプタなのにいちいちConnection.Close()をするかというと、GetData()の中の処理で、Fill()を呼び出しているから。Fill()の処理では、ConnectionがOpen()していないとOpen(),SQL実行,Close()をして、ConnectionがOpenしていると、SQL実行のみするという仕組みのため。

Fillの動きは以下のページを参照。
http://msdn.microsoft.com/ja-jp/library/ms172152(VS.80).aspx

なので、上のプログラムでも、分散トランザクションにしないために、最初からConnection.Open()をしておけばよかったことになる。
つまり、上のプログラムを書き直すと、こんな感じ。
using (TransactionScope ts = new TransactionScope())
{
DataSet1.ProjectDataTable dt = new DataSet1.ProjectDataTable();
using (ProjectTableAdapter ta = new ProjectTableAdapter())
{
ta.Connection.Open();
try
{
dt = ta.GetData();
dt = ta.GetData();
}
finally
{
ta.Connection.Close();
}
}
}

なお、違うクラスのテーブルアダプタでも、接続先DBや接続方法が同じであれば、以下の書き方のように、接続を使いまわすことにより、分散トランザクションへの昇格を回避できます。
using (TransactionScope ts = new TransactionScope())
{
DataSet1.ProjectDataTable dt1 = new DataSet1.ProjectDataTable();
DataSet2.TaskDataTable dt2 = new DataSet2.TaskDataTable();
using (ProjectTableAdapter ta1 = new ProjectTableAdapter())
{
using (TaskTableAdapter ta2 = new TaskTableAdapter())
{

ta1.Connection.Open();
try
{
dt1 = ta1.GetData();
ta2.Connection = ta1.Connection;
dt2 = ta2.GetData();
}
finally
{
ta1.Connection.Close();
}
}
}
}
ただ、テーブルアダプタのConnectionプロパティは、internal指定なので、テーブルアダプタと同じアセンブリからでしか、参照、設定が出来ないので、DLLで分離している場合は注意が必要。

windowsのドライブを分けるとしたら、インストール先の判断はどうすればいいんだろう?

http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1015481010
の記事や、
http://www.microsoft.com/japan/athome/magazine/ucontents/users/winXP/0503/default.aspx
の記事を見て、何か気になった。
上の情報では、Windowsや、ソフト本体のインストールは、Cドライブにインストールして、データをDドライブに入れるべき、と書いてある。私も同意しますが、会社では、Cドライブは極力小さくして、SQL Server本体や、Visual Studio本体もDドライブに分けた方がいいという人もいる。(Cドライブがそれほど小さいわけでもないのに)
その人に何度聞いてもメリットを理解できなかったのだけれど、1番上の質問者もそうなので、何か本当の意味があるのかもしれません。何だろう???

といっても私は、Cドライブ1本で、最重要なファイル(家ではデジカメ画像、会社では開発物など)は外部の共有フォルダにおいて、それ以外のもの(メールぐらいまで)は、消えたら諦めるという立場をとってますが、、、そんなんで情報リテラシーが低いなんて言わないでね。

テーブル設計その3?

http://remindbook.blogspot.com/2009/10/blog-post_3083.html
で、コメントをいただいたので、もうちょっとだけ考えてみました。

とその前に、最初に言い訳。
昔、以下の記事で、テーブル設計をした際、
http://remindbook.blogspot.com/2008/06/blog-post.html
やっちゃ駄目といったIdや、Nameなど、バリバリに使っていますが、あれはすごく小さなデータベースなので、本当にあれで業務アプリを作ってはいけないと思ってます。
あんなのは説明するために作ったテーブルに過ぎません。まあ何とか外部キー定義しているので、クエリビルダ(Accessや SQL Server ManagementStudioのView作成や、Visual Studio のテーブルアダプタ作成などで出てくるもの)にProjectとTaskをおいたときにそれぞれIdで自動で連結されるということは無いのですが、外部キーが張ってなかったら、テーブルをおいた直後にId列同士でJoinされます。

で、本題。1人で作る業務データベースなら、列名はPersonIdなど一般的なものではなく、業務で意味ある名前AuthorIdとするべきだと思います。
列がカードだと思うと、カードを束ねるクリップのようなものがテーブル名で、クリップを外してバラバラにした後に、別のクリップでカードを束ねたときにも、なんとなく分るような列名にしたほうがいいんじゃないかな?と思っています。
列名を一般名にして、テーブル結合の際に列名変更を強制するというSQL作成ルールを作っても、きっと誰も守ってくれないと思うので、そんなルールは作るべきではないと思うし。。

まぁ、業務なんてどんどん変わっていくものなので、時には列名が決めにくいということはあるとは思いますが、データは、処理ほど頻繁に変わるわけではないし、ある程度変更を許容できるように設計するはずなのでなんとなくうまくいくんじゃないかなあ?


ただ、テーブル設計をする人が複数人になった場合は、列名など些細なことにこだわってられません。(テーブル数が数百~数千となり、列数が一万を超えたりしますから。)
そうなった場合は、各テーブルの管理はそれぞれのチームが管理することとして、DB共通ルールは、ドメインとキーのみ見ることになると思います。ドメインでは、ユーザー名は○桁の文字列/性別は1が男,2が女のtinyintなど/のルールに従った設計になっているかというドメインチェックと、主キーと外部キーが正しいか?という寒天だけになると思います。
ただ、ドメイン設計をしても、ユーザ定義型(create type)は絶対に作るべきではないと思ってます。そんなに人は賢くないので、ユーザ定義型を導入すると、ユーザ定義型を使っている部分/使っていない部分が混在するか、同じようなユーザ定義型ができるに決まってるから。
同じ意味でスキーマ名をつけることはないんじゃないかなと思います。今まで携わったプロジェクトでは、すべて、すべてdbo.テーブル名にしちゃってました。