質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • C#

    8536questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • SQLite

    750questions

    SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

C# Sqliteにインサートする際、既にキーがかぶるデータが存在する場合の対処方法について

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,378

GiveAHand

score 268

いつも大変助けて頂いております。
感謝しております。

今、C#からsqliteにデータをインサートするプログラムを作成しています。

今、例として、このようなテーブルに、

CREATE TABLE `TABLE_SAMPLE` (
    `ID`    INTEGER NOT NULL,
    `DATA`    TEXT NOT NULL,
    PRIMARY KEY(ID)
);

このようなプログラムでインサートをしているのですが、

//SQLiteにデータ書き込み
using (SQLiteConnection cn = new SQLiteConnection(dbConnectionString))
{
    cn.Open();

    try
    {
        // CSV インサート
        using (SQLiteTransaction trans = cn.BeginTransaction())
        {

            SQLiteCommand cmd = cn.CreateCommand();

            // インサート文
            cmd.CommandText = "INSERT INTO TABLE_SAMPLE (ID, DATA) VALUES (@id, @data)";

            // パラメータのセット
            cmd.Parameters.Add("id", System.Data.DbType.UInt16);
            cmd.Parameters.Add("data", System.Data.DbType.String);

            try
            {

                foreach (SelectResult csvfile in result)
                {

                    // csvファイルを開く
                    using (var sr = new System.IO.StreamReader(@csvfile.filepath))
                    {

                        int csvcont;
                        csvcont = 0;

                        // ストリームの末尾まで繰り返す
                        while (!sr.EndOfStream)
                        {

                            csvcont++;

                            // ファイルから一行読み込む
                            var line = sr.ReadLine();
                            // 読み込んだ一行をカンマ毎に分けて配列に格納する
                            var values = line.Split(',');
                            var ii = 0;

                            // 出力する
                            foreach (var value in values)
                            {

                                switch (ii)
                                {
                                    case 0:
                                        cmd.Parameters["id"].Value = int.Parse(value);
                                        break;
                                    case 1:
                                        cmd.Parameters["data"].Value = int.Parse(value);
                                        break;
                                    default:
                                        break;
                                }

                                ii++;

                            }

                            cmd.ExecuteNonQuery();

                        }

                    }

                }
            }
            catch (System.Exception e)
            {
                // ファイルを開くのに失敗したとき
                LogOutput(e.Message, true, true);
            }

            // コミット
            trans.Commit();

        }

    }
    catch (Exception exception)
    {
        LogOutput(exception.Message, true, true);
        return false;
    }

}

当然ながら、プライマリーキーであるIDが重複したデータをインサートしようとするとエラーになります。

これをなんとか、プライマリーキーが重複した場合はインサートしないようにするか、または、構わずインサートをして、エラーが起こっても、その後の処理が継続するようにしたいと思っています。

このような場合、インサート前にselectでデータカウントをしてみないといけないのでしょうか?

データ件数が多いので、1データインサートするごとにSELECTするのは、時間がかかるのではないかと思い、心配です。

また、このインサートは、CSVファイルを読み込んでデータ分ループさせているのですが、エラーでcatchに入ってしまうと、forループがそこで終わってしまうので、うまくいきません。

例えば、プライマリーキーが重複する場合は、インサートせずにエラーにもしないような、何かいい方法は無いでしょうか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

INSERT前に主キーをチェックする方法としては、仰るとおり該当のIDをSELECTで取得する必要があります。
ご心配されている処理時間については、主キーを検索条件にするのであればそれほど時間はかからないのでは、と思いますが…

毎回SELECTを発行するのが嫌だというのであれば、IDの一覧を配列なりListなりDataTableなりに取得しておいて、INSERTの前に参照する方法もあります。
ただし、ID一覧を取得した後、他の処理からテーブルのデータが変更されないようにロックを取得する必要があります。

また、このインサートは、CSVファイルを読み込んでデータ分ループさせているのですが、エラーでcatchに入ってしまうと、forループがそこで終わってしまうので、うまくいきません。

とのことですが、コードを見る限りではINSERTで例外が発生しても処理が途中で終了するようには見えません。
forループ含めたコードも記載していただけますか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/06/04 14:47 編集

    gusao様

    お世話になります。
    やはり、1件1件selectで確認していくのが確実でしょうか。

    今、ソースコードを修正して、関数の中身全部書いてみました。
    ライブラリに収められたファイル分だけforeachでループし、
    中身を1行1行インサートしています。

    1件でもインサートにすると、このforeachの外に出てしまうので、そこでcommitされて終了となります。

    この場合、cartchの位置などを変えて、インサートで失敗しても、そのまま次の一行を取得出来るように出来るものなのでしょうか?

    キャンセル

  • 2016/06/04 15:08 編集

    cmd.ExecuteNonQuery();
    をtryで囲み、catchで例外を握りつぶしてしまえば、
    INSERTに失敗してもループ処理は続行すると思います。
    単純に握りつぶすだけではINSERTに失敗したか判断できないので、
    catch処理の中でログに吐く必要はあると思いますが。

    ただしこの場合だとtryが3重になってしまうので
    あまり見栄えはよろしくないですが…
    あ、でも一行だけしかtryで囲まないので見栄えは問題ないかもしれません。

    キャンセル

  • 2016/06/04 16:06

    gusao様

    うまいくいきました!!!
    本当に助かりました!!!
    ありがとうございました!!!

    キャンセル

0

そもそも重複しないIDをつければいいのではないでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/06/04 14:48

    Zuishin様

    うーん、なかなかそういう訳にもいきませんで。。。

    キャンセル

  • 2016/06/04 14:57

    では、これから挿入するIDをin句で検索してヒットしたものを除くということでどうでしょう?

    select id from table where id in (1,7,50,52)

    キャンセル

  • 2016/06/04 15:08

    しかし思いますにやはりIDが不定という設計が悪いと思われます。

    現在あるIDの他に真のID(仮にTID)を作り、これは重複しないようデータの挿入順に番号をつけます。
    そして現在のIDを重複可能な単なるフィールドとして扱います。

    そして全て挿入が終わった時点でIDが重複したものだけ抽出して余分を削除します。
    これが一番早いのではないでしょうか?

    キャンセル

  • 2016/06/04 16:07

    Zuishin様

    うーん、ちょっとそういう事ではございませんで。。。
    すいません。

    いろいろとお考え下さいまして、
    ありがとうございました。

    キャンセル

同じタグがついた質問を見る

  • C#

    8536questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • SQLite

    750questions

    SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

  • トップ
  • C#に関する質問
  • C# Sqliteにインサートする際、既にキーがかぶるデータが存在する場合の対処方法について