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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

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

SQLite

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

Q&A

解決済

2回答

10009閲覧

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

GiveAHand

総合スコア286

C#

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

SQLite

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

0グッド

0クリップ

投稿2016/06/04 04:00

編集2016/06/04 05:43

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

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

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

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

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

C#

1//SQLiteにデータ書き込み 2using (SQLiteConnection cn = new SQLiteConnection(dbConnectionString)) 3{ 4 cn.Open(); 5 6 try 7 { 8 // CSV インサート 9 using (SQLiteTransaction trans = cn.BeginTransaction()) 10 { 11 12 SQLiteCommand cmd = cn.CreateCommand(); 13 14 // インサート文 15 cmd.CommandText = "INSERT INTO TABLE_SAMPLE (ID, DATA) VALUES (@id, @data)"; 16 17 // パラメータのセット 18 cmd.Parameters.Add("id", System.Data.DbType.UInt16); 19 cmd.Parameters.Add("data", System.Data.DbType.String); 20 21 try 22 { 23 24 foreach (SelectResult csvfile in result) 25 { 26 27 // csvファイルを開く 28 using (var sr = new System.IO.StreamReader(@csvfile.filepath)) 29 { 30 31 int csvcont; 32 csvcont = 0; 33 34 // ストリームの末尾まで繰り返す 35 while (!sr.EndOfStream) 36 { 37 38 csvcont++; 39 40 // ファイルから一行読み込む 41 var line = sr.ReadLine(); 42 // 読み込んだ一行をカンマ毎に分けて配列に格納する 43 var values = line.Split(','); 44 var ii = 0; 45 46 // 出力する 47 foreach (var value in values) 48 { 49 50 switch (ii) 51 { 52 case 0: 53 cmd.Parameters["id"].Value = int.Parse(value); 54 break; 55 case 1: 56 cmd.Parameters["data"].Value = int.Parse(value); 57 break; 58 default: 59 break; 60 } 61 62 ii++; 63 64 } 65 66 cmd.ExecuteNonQuery(); 67 68 } 69 70 } 71 72 } 73 } 74 catch (System.Exception e) 75 { 76 // ファイルを開くのに失敗したとき 77 LogOutput(e.Message, true, true); 78 } 79 80 // コミット 81 trans.Commit(); 82 83 } 84 85 } 86 catch (Exception exception) 87 { 88 LogOutput(exception.Message, true, true); 89 return false; 90 } 91 92}

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

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

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

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

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

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

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

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

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

投稿2016/06/04 04:34

gusao

総合スコア185

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

GiveAHand

2016/06/04 05:47 編集

gusao様 お世話になります。 やはり、1件1件selectで確認していくのが確実でしょうか。 今、ソースコードを修正して、関数の中身全部書いてみました。 ライブラリに収められたファイル分だけforeachでループし、 中身を1行1行インサートしています。 1件でもインサートにすると、このforeachの外に出てしまうので、そこでcommitされて終了となります。 この場合、cartchの位置などを変えて、インサートで失敗しても、そのまま次の一行を取得出来るように出来るものなのでしょうか?
gusao

2016/06/04 06:14 編集

cmd.ExecuteNonQuery(); をtryで囲み、catchで例外を握りつぶしてしまえば、 INSERTに失敗してもループ処理は続行すると思います。 単純に握りつぶすだけではINSERTに失敗したか判断できないので、 catch処理の中でログに吐く必要はあると思いますが。 ただしこの場合だとtryが3重になってしまうので あまり見栄えはよろしくないですが… あ、でも一行だけしかtryで囲まないので見栄えは問題ないかもしれません。
GiveAHand

2016/06/04 07:06

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

0

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

投稿2016/06/04 05:28

Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

GiveAHand

2016/06/04 05:48

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

2016/06/04 05:57

では、これから挿入するIDをin句で検索してヒットしたものを除くということでどうでしょう? select id from table where id in (1,7,50,52)
Zuishin

2016/06/04 06:08

しかし思いますにやはりIDが不定という設計が悪いと思われます。 現在あるIDの他に真のID(仮にTID)を作り、これは重複しないようデータの挿入順に番号をつけます。 そして現在のIDを重複可能な単なるフィールドとして扱います。 そして全て挿入が終わった時点でIDが重複したものだけ抽出して余分を削除します。 これが一番早いのではないでしょうか?
GiveAHand

2016/06/04 07:07

Zuishin様 うーん、ちょっとそういう事ではございませんで。。。 すいません。 いろいろとお考え下さいまして、 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問