2008年4月25日金曜日

[他]IDENTITYフィールドに指定の値を突っこむ(SQLServer)

SQLServerでは、フィールドに対してIDENTITYという属性を付けることができます。この属性を設定すると、INSERT時に自動的に値を設定してくれます。MS-Accessでいうところのオートナンバー型ですね。

しかし、事故など(間違えでDELETEしたなど)があると、値を指定できないためそのレコードを復元することができません。たとえばこの値を別のテーブルの値として設定している場合など、データを復元するためにかなりの労力が必要となります。

過去、そういう場合あきらめていましたが、IDENTITY属性のあるフィールドに無理やり値を指定する方法があることがわかりました。リファレンスはこちら。
コードはこんな感じ。

SET IDENTITY_INSERT table1 ON;
INSERT INTO table1(pid, tex1, tex2)values(800,'1','2')
SET IDENTITY_INSERT table1 OFF;


リファレンスをみると、いろんなSET文があります。一通り眺めてみると勉強になりそうですね。

[.NET]INSERT時の自動採番される値を取得する(SQLSERVER)(その2)

GDD Blog: [.NET]INSERT時の自動採番される値を取得する(SQLSERVER)で、自動採番した値を取得する方法について書きましたが、たとえば、同一トランザクション内で自動採番されるテーブルが2つ以上あり、その2のテーブルで採番された値を後方の処理で利用する必要がある場合、SCOPE_IDENTITY()では最後のINSERTで生成した値しか取れません。でどうするかというと、IDENT_CURRENT('table_name')を使います。

■テーブルの構成
CREATE TABLE [TABLE1] (
    [pid] [int] IDENTITY (1, 1) NOT NULL ,
    [tex1] [char] (10) COLLATE Japanese_CI_AS NULL ,
    [tex2] [char] (10) COLLATE Japanese_CI_AS NULL ,
    CONSTRAINT [PK_TABLE1] PRIMARY KEY  CLUSTERED 
    (
        [pid]
    )  ON [PRIMARY] 
)
GO


■実行したSQL
begin transaction

insert into table1(tex1, tex2)values('1','2')

print '■table1'
SELECT  '@@IDENTITY' , @@IDENTITY 
SELECT  'SCOPE_IDENTITY' , SCOPE_IDENTITY()
SELECT  'IDENT_CURRENT' , IDENT_CURRENT('table1')

insert into table2(tex1, tex2)values('1','2')
print '■table2'
SELECT  '@@IDENTITY' , @@IDENTITY 
SELECT  'SCOPE_IDENTITY' , SCOPE_IDENTITY()
SELECT  'IDENT_CURRENT' , IDENT_CURRENT('table1')

rollback


■実行結果
■table1
@@IDENTITY    9
SCOPE_IDENTITY    9
IDENT_CURRENT    9

■table2
@@IDENTITY    1
SCOPE_IDENTITY    1
IDENT_CURRENT    9


※table1は8件、table2は0件の状態です。

(注意)GDD Blog: [.NET]INSERT時の自動採番される値を取得する(SQLSERVER)(その3)で問題点を記載しています。こちらも参照してください。

2008年4月23日水曜日

[.NET]OracleClient(その2)

「GDD Blog: [.NET]OracleClient」で記載しましたODP.NETですが、なんと、Vista環境では自動トランザクション機能(mts)がサポートされていない事がわかりました。
10gだけではなく、最新の11gで提供されているODP.NETも同様のようです。

私の中では、DB使うシステムでは、自動トランザクションは必須なんですけど、これからVistaが普及しだしたら、このままだと開発できないことになっちゃいます。

Oracleが対応するのかどうか傾向もわかりません。はてどう考えればいいものか。。。

2008年4月18日金曜日

[.NET]XmlTextWriterのチョイ技

XmlTextWriterはXMLを出力する操作を助けてくれるクラスです。そしてDomのように重たくなく、且つ直感的な操作で、XMLを作成することができます。

しかし、なぜか「encoding="utf-16"」になってしまいす。 たとえば「encoding="utf-8"」にしたい場合は、WriteProcessingInstructionメソッドにて指定が可能です。(コードのコメント部分を生かす)
また、xml宣言そのものを出したくないといった場合は、「XmlWriterSettings.OmitXmlDeclaration」の指定にて出力しない方法があります。コードはこんな感じ


StringBuilder sb = new StringBuilder();

XmlWriterSettings xs = new XmlWriterSettings();
xs.Indent = true;

using (XmlWriter xw = XmlTextWriter.Create(sb, xs))
{
    //xw.WriteProcessingInstruction("xml", "version='1.0' encoding='utf-8'"); //エンコードが指定
    xw.WriteStartElement("ROOT");

    xw.WriteStartElement("AAA");
    xw.WriteAttributeString("aa1", "123");
    xw.WriteAttributeString("aa2", "456");
    xw.WriteString("ああああ");
    xw.WriteEndElement();//</AAA>

    xw.WriteStartElement("BBB");

    xw.WriteStartElement("BBB1");
    xw.WriteString("いいいい1");
    xw.WriteEndElement();//</BBB1>

    xw.WriteStartElement("BBB2");
    xw.WriteString("いいいい2");
    xw.WriteEndElement();//</BBB2>

    xw.WriteEndElement();//</BBB>

    xw.WriteEndElement();//</ROOT>

    xw.Close();
}


<?xml version="1.0" encoding="utf-8"?>
<ROOT>
  <AAA aa1="123" aa2="456">ああああ</AAA>
  <BBB>
    <BBB1>いいいい1</BBB1>
    <BBB2>いいいい2</BBB2>
  </BBB>
</ROOT>

2008年4月14日月曜日

[.NET]INSERT時に重複レコードを取り除く

たまに、テーブルからテーブルへデータをコピーしたいときがあります。そういう時以下のようなSQLを実行します。

INSERT INTO T_ADRESS_1
  SELECT * FROM T_ADRESS_2
  WHERE
  T_ADRESS_2.ADDRESS LIKE '大阪府%'


これは、T_ADRESS_2のADDRESSが「大阪府」で始まるレコードをT_ADRESS_1にコピーするというSQLになります。

しかし、T_ADRESS_1にすでに登録されている情報がある場合、キー重複エラーになったり、不要なレコードが増えてしまいます。

1件ずつチェックしながらデータをチェックしながらコピーすれば確実なんですが、件数が多い場合パフォーマンスが出ません。

で、よく考えてみると、INSERTする前にT_ADRESS_1を同じ条件でDELETEしておけば。。。ということにいまさら気がつきました。。。その他、NOT EXISTを使う方法もありますね。

2008年4月13日日曜日

[他]コーディング規約

ソフト開発では、1つのシステム開発を複数の会社が集まって開発することがよくあります。で、そういった場合は、大体その中の1社が幹事会社となり、いろんなことを決めます。たとえば設計書のフォーマットなど。

設計書のフォーマットもモメますが、問題はコーディング規約です。 後のメンテを考えると尾を引きます。
機能ID+連番みたいなクラス名命名だったり、.NET系の開発なのに、C言語風のメソッド(関数?)命名であったり、10年くらい前にVC++で採用されていたような変数名のつけ方を採用するなど、今時の統合開発環境であれば変数名から類推する必要もない事を規約にされた場合、開発するときのテンションがすごく下がっちゃいます。 特に変数名のつけ方はアレですね。

そこで何とか今風にできないかと考えたのですが、「ms社にそれらしい規約があれば説得力があるのでは?」と同僚に言われ早速検索。すると、以下のようなものを見つけました。
前回は、ドキュメントのフォーマットを押し付けられたので、「Visual Basic のコーディング規則 」をもとに(ちょっと微妙)。。。でも「Visual Basic の名前付け規則」ちょっと弱い。けどプレフィックスについての記述もないし出元がms社なので、意外と説得力があるかも。。。

2008年4月10日木曜日

[.NET]例外の処理

最近のオブジェクト指向言語には、言語仕様に例外処理の機構が組み込まれています。 c#の場合、こんな感じですね。

try
{
    //例外の発生する可能性のある処理
}
catch (Exception ex)
{
    //例外の出た場合の処理
}
finally
{
    //リソースの開放処理
}


しかし、処理の方法が人によりまちまちで、困る場合があります。
たとえば、
  • 入力チェックに例外処理を利用している
  • 事前にチェックすれば発生しない例外の考慮をしている
  • 握りつぶしてはマズイ例外を握りつぶしている
  • 例外処理をしているが、資源の解放漏れがある
  • そもそも例外の発生を考慮していない

などあります。そこで、何かいいガイドラインは無いかと思って調べてみると、MSDNにそれらしいことが記載してありました。
その中で「Exception 基本クラスからユーザー定義例外を派生しないでください。ほとんどのアプリケーションでは、ApplicationException クラスからカスタム例外を派生します。 」
とありますが、@it掲示板によるとApplicationExceptionからの派生はあまりよくないとのことらしい。

私の解釈は、以下のとおりです。

  • 握りつぶしてよい例外以外はcatchしない
  • (基本的に無いけど)catchした場合、その例外をまたはシステム共通の例外をthrowしなおす
  • 例外処理は一箇所で行う。同時にここでログ(デバッグ用)を出力する(ASP.NETの場合、Application_Error。WindowsFormの場合Application.ThreadExceptionで処理する)

例外のハンドリング方法としてはちょっと古いかも知れませんが、まぁコレが今のところ一番いいかなぁと思っています。

ちょっといまさら感がありますが、例外処理に関してはEnterprise Library(EHAB)があります。今度こっちを調査いてみよう。



あ、資源の解放という点ではfinallyよりusingで処理します(C#)。