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

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

ただいまの
回答率

89.98%

vb.net select文実行後のinsert文で失敗する

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 4,975

chintao1224

score 87

win7(64bit)、vb2017(community版)、postgresql6.3、npgsqlを使っています。
select文を実行し、データがなければinsert文を実行するというコードを書いています。

        Dim cn As New NpgsqlConnection
        Dim scmd As New NpgsqlCommand
        Dim icmd As New NpgsqlCommand
        Dim dr As NpgsqlDataReader
        Dim trn As NpgsqlTransaction
        Dim intCount As Int32
        Dim strSSQL As String
        Dim strISQL As String
        Dim strDateTime As String
        Try
            cn.ConnectionString = 接続文字列
            cn.Open()
            trn = cn.BeginTransaction()
            strSSQL = セレクト文
            scmd.Connection = cn
            scmd.CommandText = strSSQL
            dr = scmd.ExecuteReader()
            If (dr.HasRows = False) Then
                strISQL = インサート文
                icmd.CommandText = strISQL
                intCount = icmd.ExecuteNonQuery
                trn.Commit()
            Else
                trn.Rollback()
            End If
        Catch ex As Exception
            trn.Rollback()
            strDateTime = Format(Now, "yyyy-MM-dd hh:mm:ss")
            Call sWriteErrorLog(strDateTime, "DB選択", ex.ToString)
        Finally
            cn.Close()
            dr = Nothing
            scmd = Nothing
            icmd = Nothing
            cn = Nothing
        End Try

このコードではintCount = icmd.ExecuteNonQueryでエラーになりex.tostringには沢山書かれていますが、先頭に「System.InvalidOperationException: Connection property has not been initialized.」と書かれています。
cnは初期化したつもりはないのですが。。。

それと何故かcatchに行った後trn.Rollback()でエラーになります。
「A command is already in progress」
例外処理でrollbackはできないのでしょうか?

上記2つのことをお聞きしたいです。

ちなみにselect文とinsert文はツールのA5:SQL Mk-2で実行して問題なく実行できます。

よろしくお願いします。

教えていただいた情報を読みコードを書いてみました。

        Using cn As NpgsqlConnection = New NpgsqlConnection()
            cn.ConnectionString = 接続文字列
            cn.Open()
            Dim strSelectSQL As String = セレクト文
            Dim nSelectCmd As NpgsqlCommand = New NpgsqlCommand(strSelectSQL, cn)
            Dim dr As NpgsqlDataReader = nSelectCmd.ExecuteReader
            If (dr.HasRows = False) Then
                'Dim trn As NpgsqlTransaction = cn.BeginTransaction()
                Try
                    Dim strInsertSQL As String = インサート文
                    Dim nInsertCmd As NpgsqlCommand = New NpgsqlCommand(strInsertSQL, cn)
                    Dim intCount As Int32 = nInsertCmd.ExecuteNonQuery()
                    'trn.Commit()
                    nInsertCmd.Dispose()
                Catch ex As Exception
                    'trn.Rollback()
                    Dim strDateTime As String = Format(Now, "yyyy-MM-dd hh:mm:ss")
                    Call sWriteErrorLog(strDateTime, "DB選択", ex.ToString)
                Finally
                    dr.Dispose()
                    nSelectCmd.Dispose()
                End Try
            End If
        End Using

コメントアウトしていますが、Dim trn As NpgsqlTransaction = cn.BeginTransaction()でエラーになります。

またDim intCount As Int32 = nInsertCmd.ExecuteNonQuery()でcatchに行き以下のようなエラーが出ます。

「"A command is already in progress:  セレクトのSQL文"」

vb.netが初心者に近いので解らないのですが。

何が良くないのでしょうか?
なぜトランザクションがエラーになるのでしょうか?

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+1

色々問題あるように思えます。

まず、connectionをOpenしたら必ずCloseしないといけないです。
ということはOpenしていなければCloseしてはいけません。

提示されたコードを見ると、Openに失敗したらどうなりますか?
Catchでロールバックをしますが、そもそもBeginTransactionもされていないのでRollBackできませんね。
FinallyでCloseしますが、OpenしてないのでCloseもできませんね。

1 Usingを使ってConnectionをOpenすればCloseを明示しなくても勝手にCloseします。
2 BeginTransactionが成功した場合、TryCatchでRollBackしましょう。

あとは
SQlCommandもReaderもConnectionもIDisposableを実装しているクラスは必ずDispose()しないとだめです。
Usingを使ってDisposeを保障してください。
Classにカーソルを当ててF12を押して定義をさかのぼってIDisposableを宣言しているかどうか確認しながら実装してください。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/07/30 18:27

    ソースを変更して実行したのですが、だめでした。よろしくお願いします。

    キャンセル

  • 2017/07/30 19:30

    vb.netではselectしてinsertするなんて関数作れないのでしょうか?

    キャンセル

  • 2017/07/30 19:38

    存在チェックのためのSQLであれば

    select count(*) from table where 条件

    ですよね。

    ExecuteReaderではなくてExecuteScalarでカウントを取得すればいいのでは?
    勘だけどReaderをDisposeしてないのにInsertしてるから怒られてるんじゃないの?
    あとFinallyでDisposeするのはかなり古いです。Connection同様全部Usingで囲ってください。

    キャンセル

  • 2017/08/16 12:07

    遅くなりまして申し訳ございません。usingを使って何とかできました。ありがとうございました。

    キャンセル

+1

Tak1016さんの回答が正しそうなので、あえて違う観点からコメントしてみます。

Npgsqlライブラリがこの構文をサポートしているか分かりませんが、まずは1つのSQLで実現できないかを考えた方がいいと思います。PostgreSQLであれば以下のようなSQLで実現できます。

INSERT INTO example_table
    (id, name)
SELECT 1, 'John'
WHERE
    NOT EXISTS (
        SELECT id FROM example_table WHERE id = 1
    );

Postgres: INSERT if does not exist already

また、「selectしてinsertする関数」はもちろん作れますが、テストの観点からは「①sqlを実行する関数」「②select(*)でレコード件数を返却する関数」、「③insertする関数」、「④select(*)とinsertの2つの関数を呼び出す関数」等に分けた方がスッキリするかもしれません。①に対して②、③はクエリ(オブジェクト)を引数として渡すイメージです。これはあくまで一例ですが、この他にも色々なところにSQLを実行するコードが散財してそうかな...?と思いまして。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/08/16 12:09

    たしかにSQL文の書き方でできますね。勉強になりました。ありがとうございました。

    キャンセル

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

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

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