C#フォームでSQLite。他のDBでも通用するINSERT文とは…?
- 評価
- クリップ 0
- VIEW 2,747

退会済みユーザー
Qiitaと同様の投稿内容となっております。
ご容赦ください。
※※※※※※※※※※※※※※※※※※※※※
C#でSQLiteを扱うプログラムを書いていて、お偉いさんにもらった指摘からどう改良していけばいいかで詰んでるお話。
何をするのか
現存するテーブル「TABLE」に、・ID:テーブル内の最高値の次の番号
・NAME:TextBoxから入力
・POINT1~3:NumericUpDownから入力
をINSERTでレコードとして追加していくだけなので、単純といえば単純。
とりあえず組んでみた
/// <summary>
/// レコードを追加
/// </summary>
private void recordAdd()
{
// 宣言
SQLiteConnection cn = null;
try
{
// DB接続文字列
string connectionString = string.Format("Data Source={0};Version=3;", Application.StartupPath + @"\result.db");
// DB Open
cn = new SQLiteConnection(connectionString);
cn.Open();
SQLiteCommand cmd = cn.CreateCommand();
cmd.CommandText = string.Format("INSERT INTO TABLE(ID, NAME, POINT1, POINT2, POINT3) SELECT MAX(ID) + 1, {0}, {1}, {2}, {3} FROM TABLE;",
"'" + this.textBox_Name.Text + "'",
this.numericUpDown_Point1.Value.ToString(),
this.numericUpDown_Point2.Value.ToString(),
this.numericUpDown_Point3.Value.ToString());
cmd.ExecuteNonQuery();
}
finally
{
// DBクローズ
if (cn != null)
{
cn.Close();
}
}
}
…とまあ、こんな感じで。実行中も特にエラーは起きなかったので、これで提出すると…。
お偉いさんからの返事
SQLiteはプライマリーキーに数字が割り当てられている>と自動で一番大きな値+1をしてID発行するようになっている。
データがない場合MAXはnullになることを考慮しているかは不明だが、たまたまうまく動作している原因となっている。
nullを考慮したSQLもしくはプログラムを組んでほしい(他のDBでも同じようなSQLを書いてしまうのを防ぐため)。
…どゆこと?
MAX(ID) + 1
と書くのがよろしくないのか…?
テーブルが空であるときにIDの最高値を見てもnullだから、SQLiteはともかく他のDBで同様に動作する保証はないので、
他のDBでも問題なく動作しそうなコードに改良しろ、ということか…。
当然、INSERTするIDはDBから取得して決定するのが普通だろうから、
DBの中身が空であるかどうかを判定すればいいのか。
で、どっちをいじるべきかな…と迷いだす。
ちょっと考えればif文とかで強引に組めるんだろうけど、SQL文ちょっといじるだけで突破できるならそうしたいよねっていう。
…どなたかご教授ください…(;꒪ ω꒪)
追記 3/18 11:30
お偉いさんに、IDを指定せずにオートインクリメントで付けいていくのは、仕様に甘えている。とのお言葉をいただいて参りました。なんじゃそりゃ。
たしかに普通はオートインクリメントを設定しておくものだが、
今回に限りその設定がないものとして考えてみてくれ。
bm000999さんのコメントにある、
ISNULL(MAX(ID) + 1, 0)
を利用してみようと思いましたが、そのまま組み込んで、
INSERT
INTO TABLE
( ID
, NAME
, POINT1
, POINT2
, POINT3 )
SELECT
ISNULL(MAX(ID) + 1, 0)
, {0} /* TextBox */
, {1} /* NumericUpDown */
, {2} /* NumericUpDown */
, {3} /* NumericUpDown */
FROM
TABLE
;
としてみたのですが、try-catch
で例外を返されてしまいました。
SQL logic error or missing database near "ISNULL": syntax errorあれ、クエリが間違ってる…?もしかしてINSERT文のときは
ISNULL
を使えないとか…?
というかそもそもSQLiteに
ISNULL
って存在するのか…?
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
+2
TABLE
テーブルが適切に定義されていれば
cmd.CommandText = string.Format(
"INSERT INTO TABLE(NAME, POINT1, POINT2, POINT3) VALUES({0}, {1}, {2}, {3});",
"'" + this.textBox_Name.Text + "'",
this.numericUpDown_Point1.Value.ToString(),
this.numericUpDown_Point2.Value.ToString(),
this.numericUpDown_Point3.Value.ToString());
で動作する、という話だと思います。投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
check解決した方法
+1
INSERT
INTO TABLE
( ID
, NAME
, POINT1
, POINT2
, POINT3)
SELECT
CASE
WHEN COUNT(*) = 0 THEN 1
ELSE MAX(ID) + 1
END
, {0} /* TextBox */
, {1} /* NumericUpDown */
, {2} /* NumericUpDown */
, {3} /* NumericUpDown */
FROM
TABLE
;
最初からこうすればよかった…。
なにはともあれ解決。結果もごくごく単純なものでした。
皆様、ご協力ありがとうございました。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
SQLiteはプライマリーキーに数字が割り当てられている>と自動で一番大きな値+1をしてID発行するようになっている。まずはSQLiteの仕様を理解されるといいと思います。
ここ、分かりやすいかも。
http://shobon.hatenablog.com/entry/2014/03/30/210444
オートインクリメントで勝手にID発行するから、IDを指定することで整合性が保てなくなる。
だからINSERT文を叩くときにIDに値を設定してくれるな、ということです。
当然、INSERTするIDはDBから取得して決定するのが普通だろうから、結果的に、これはDBエンジンおよび仕様による、ということになります。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
2015/03/18 05:41
たとえば、SQLserverに対して、データのないテーブルに、MAX(ID) + 1 ですとNULLが返って来てそのままnull + 1 でnullがインサートされます。ですので、ISNULL(MAX(ID)+ 1, 0)で対応できなくもないですが、ほぼ同時にインサートが発生した時など数値が重複する可能性があります。
インクリメントはテーブルに委ねると整合性が保たれます。通常、列にシーケンスを定義します。インサート時は、インクリメント対象列は指定しません。各DBの設定方法については、[こちら](http://atamoco.boy.jp/oracle/20110414_2.php)を参考にしてください。そういった意味で、テーブルの対象の列を定義する必要があります。