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

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

ただいまの
回答率

90.48%

  • C#

    7449questions

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

  • SQLite

    641questions

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

C#フォームでSQLite。他のDBでも通用するINSERT文とは…?

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,553
退会済みユーザー

退会済みユーザー

※※※※※※※※※※※※※※※※※※※※※
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ページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

+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());
で動作する、という話だと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 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)を参考にしてください。そういった意味で、テーブルの対象の列を定義する必要があります。

    キャンセル

check解決した方法

+1

結局、CASE文で書きました。

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
;
最初からこうすればよかった…。
なにはともあれ解決。結果もごくごく単純なものでした。

皆様、ご協力ありがとうございました。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/03/19 09:03

    お偉いさんの言ってることに矛盾、もしくは余計なことばかり言ってますよね。
    『SQLiteは(その仕様上)、プライマリーキーに数字が割り当てられていると自動で一番大きな値+1をしてID発行するようになっている。』
    『IDを指定せずにオートインクリメントで付けいていくのは、仕様に甘えている。今回に限りその設定がないものとして考えてみてくれ。』

    だったら、最初の前置きはなんだったのか。余計なこと言ってないで、
    SQLiteの仕様ではなく、業務仕様を伝えろよって感じです。

    ちなみにCASEだといちいち仰々しいので、
    COALESCE(MAX(ID), 0) + 1
    でいいと思います。

    キャンセル

+1

SQLiteはプライマリーキーに数字が割り当てられている>と自動で一番大きな値+1をしてID発行するようになっている。
まずはSQLiteの仕様を理解されるといいと思います。
ここ、分かりやすいかも。
http://shobon.hatenablog.com/entry/2014/03/30/210444

オートインクリメントで勝手にID発行するから、IDを指定することで整合性が保てなくなる。
だからINSERT文を叩くときにIDに値を設定してくれるな、ということです。

当然、INSERTするIDはDBから取得して決定するのが普通だろうから、 
結果的に、これはDBエンジンおよび仕様による、ということになります。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

  • ただいまの回答率 90.48%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 解決済

    【SQLite】複数カラムのデータをまとめて対象としてソートする。

    SQLiteで例えば | 名前 | 国語の点数 | 数学の点数 | 社会の点数 | 理科の点数 | 英語の点数 | のようなテーブルがあったとします。 このようなテーブルで、

  • 解決済

    SQLiteのexecuteUpdateで変数を使いたい

    SQLiteとJavaを使用して、とあるアプリケーションを作っているのですが、解決方法を教えていただきたいです。 例) String id = ReadIdForm(); Str

  • 解決済

    SQLiteで_idをクリアする方法

    SQLiteにてサーバーから取得したデータをローカルに保存する処理を 作っているのですが、サーバーとローカルの違いがあるデータだけを挿入 や削除するのは面倒なので、いったんロー

  • 解決済

    SQLiteエラーが解決できない

    お世話になります。 C#とSqliteでタスク管理を行うシステムを作成しております。 テスト的にまずはプログラム側で固定値をDBにインサートしようとしているのですが、下

  • 解決済

    sqliteにinsertする際に要素の重複を防ぐ方法

    INSERT INTO user_data VALUES (?, ?, ?) などのようにして要素をtableにinsertする際に、 すでにinsertする要素と同一の要素

  • 解決済

    sqliteに日付をinsertすると、勝手に年に変換される

    前提・実現したいこと pythonを使って日付をinsertしようとすると、年に変換されます。 text型のdateという列に "2017-08-18" を insertすると "

  • 解決済

    異なるDB間で、特定のカラムのみコピーしたい

    前提・実現したいこと 自作のAndroidアプリ内でsqliteを利用しています。 構造が同じ2つのデータベースA,Bがあり、複数テーブルが存在します。 実現したいことは、デー

  • 受付中

    DBに画像を登録したい

    お世話になります。 情報管理画面を作成しているのですが、画像登録ではまってしまっています。 128行目のパラメータの部分で CS0029    型 'System.Draw

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

  • C#

    7449questions

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

  • SQLite

    641questions

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