C# ODBC.NETでテーブルを複製するサンプル

DataReaderで取得したフィールド名等の情報を本に、SQL文(DELETE文、INSETR文)を動的に作成して、テーブル内のデータコピーを行うサンプル。

CopyTableの引数としてテーブル名、プライマリキー名の配列、コピー元データベースのConnection、コピー先データベースのConnectionを与えて下さい。

    class Program
    {
        static void Main(string[] args)
        {
            OdbcConnection srceConn = new OdbcConnection("Driver={SQL Server};Server=SV01;UID=example;PWD=;");
            srceConn.Open();

            OdbcConnection destConn = new OdbcConnection("Driver={SQL Server};Server=SV02;UID=example;PWD=;");
            destConn.Open();

            CopyTable("table_name", new string[] { "primary_key_1", "primary_key_2" }, srceConn, destConn);
            Console.ReadKey();
        }

        static void CopyTable(string tableName, string[] keyNames, OdbcConnection srceConn, OdbcConnection destConn)
        {
            Console.WriteLine("Copying {0}", tableName);

            StringBuilder insSql = null;
            Dictionary<string, int> columnIndexs = new Dictionary<string, int>();

            // DELETEコマンド文字列の生成
            StringBuilder delSQL = new StringBuilder();
            delSQL.Append(" DELETE ");
            delSQL.Append(tableName);
            delSQL.Append(" WHERE ");
            bool isFirst = true;
            foreach (var key in keyNames)
            {
                if (false == isFirst)
                {
                    delSQL.Append(" AND ");
                }
                delSQL.Append(key);
                delSQL.Append(" = ? ");
                isFirst = false;
            }

            // トランザクション開始
            var trans = destConn.BeginTransaction();

            // 複製本のデータ読み出し
            int recCnt = 0;
            OdbcCommand selectCmd = new OdbcCommand("SELECT * FROM " + tableName, srceConn);
            var rdr = selectCmd.ExecuteReader();
            while (rdr.Read())
            {
                Console.Write("record {0}\r", recCnt++);

                // フィールド位置検索用にインデックス作成
                if (columnIndexs.Keys.Count == 0)
                {
                    for (int i = 0; i < rdr.FieldCount; i++)
                    {
                        columnIndexs.Add(rdr.GetName(i), i);
                    }
                }

                // INSERTコマンド文字列の生成
                if (insSql == null)
                {
                    insSql = new StringBuilder();
                    insSql.Append(" INSERT INTO ");
                    insSql.Append(tableName);
                    insSql.Append(" ( ");
                    for (int i = 0; i < rdr.FieldCount; i++)
                    {
                        if (i > 0)
                        {
                            insSql.Append(",");
                        }
                        insSql.Append(rdr.GetName(i));
                    }

                    insSql.Append(" ) VALUES ( ");
                    for (int i = 0; i < rdr.FieldCount; i++)
                    {
                        if (i > 0)
                        {
                            insSql.Append(",");
                        }
                        insSql.Append("?");
                    }
                    insSql.Append(" ) ");
                }

                // DELETEコマンドのパラメータ設定と実行
                int keyCnt = 0;
                OdbcCommand delCmd = new OdbcCommand(delSQL.ToString(), destConn, trans);
                foreach (var key in keyNames)
                {
                    delCmd.Parameters.Add(new OdbcParameter("arg" + keyCnt.ToString("00"), rdr.GetValue(columnIndexs[key])));
                }
                delCmd.ExecuteNonQuery();

                // INSERTコマンドのパラメータ設定と実行
                keyCnt = 0;
                OdbcCommand insCmd = new OdbcCommand(insSql.ToString(), destConn, trans);
                for (int i = 0; i < rdr.FieldCount; i++)
                {
                    insCmd.Parameters.Add(new OdbcParameter("arg" + keyCnt.ToString("00"), rdr.GetValue(i)));
                }
                insCmd.ExecuteNonQuery();
            }

            Console.WriteLine("{0} Records Copyed...", recCnt);
            trans.Commit();
        }
    }

Windows 7からWindows 10への移行の難しさ

Windows 7からWindows 10への移行を、Windows XPからWindows 7への移行と同じように捉えていると、足下をすくわれる。Windows 10以降ではサポート体制や製品ライフサイクルが大きく見直されており、それに伴って従来のようにOSバージョンを固定にした塩漬け運用が難しくなっている。

Windows 10と、それ以前のMicrosoft製品との大きな違いは、OSの開発体制が移行したことにある。アジャイルデベロップメントとか、エクストリーム・プログラミングとか、継続的インテグレーションとか、ラピッドデベロップメントとか、ITの分野にいるなら聞いたことがあるはずだ。OS自体がアジャイルな開発体制に移行したことにより、Windows 10では、OSの新機能を何年かおきの新製品発売やサービスリリースを待たず、半年ごととのメジャーアップデートで提供する体制になった。

これに伴って大きく変更されたのが、アップデートを適用していないWindows 10に対するサポート提供だ。半年ごとに提供するメジャーアップデートを適用していない場合のサポート期限は18ヶ月と短い。Windows7 SP1の9年と比較しても極端に短い。

従来はOSのバージョンを固定にしたり、特定のサービスパックやWindows Updateの適用を避けることで、Windowsの機能更新にともなって表面化した不具合に対処していた場合がある。10より前は、それでもセキュリティアップデート等は提供され続けるので、OSのバージョンを塩漬けにしても問題なく運用できていたが、Windows 10以降では18ヶ月以内に対策を行うことが最低限もとめられる。

現状、新たにWindows 7を購入して使い続けているなら、相当な危機感を持って欲しい。20万円以上のパソコンの法定耐用年数は4年間、10~20万円以下なら3年間となる。2年以内に廃棄して特別損失を計上することを承知の上で、新たにWindows 7を買い続けているのは異常だ。

Windows 10の発売から既に2年以上経過し、Windows 7のサポート期限も分かっていたはずだ。にもかかわらずWindows 7にバージョンを固定し続けているというのは、Windows 10がメジャーアップデートするスケジュールに追従できるだけの開発能力が既に失われている事を示している。

自社開発していたソフトウェアを、市販のパッケージソフトウェアをそのまま使う形に置き換える事で、自社で開発するソフトウェアの数を減らす。あわせて、社内SE(特に開発寄りの)人材拡充をはかって、自社開発ソフトウェアの継続的保守開発に対応できる情報システム部署として再生をはかったほうがよい。

PS1.
Windows 10 Enterprise LTSC(Long-Term Servicing Channel)と言うランセンスを購入すれば10年間バージョンを固定にしてサポートを受けることが出来ます。一般的なProfessionalに比較して、1ライセンス辺り3万円ほど余計に支出することになりますけど。

PS2.
実はWindows 7 SP1で7年間バージョンを固定できたのは単に運が良かっただけ。もしWindows 7 SP2がリリースされていたなら、次期サービスパックリリースから24ヵ月でSP1はサポート終了していた。

参考:
Windows 7サポート終了まで2年 ユーザーはどうする?
OS にはサポート期限があります!
Windows ライフサイクルのファクト シート
https://internet.watch.impress.co.jp/docs/news/1073028.html