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

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

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

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

Q&A

解決済

2回答

7306閲覧

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

chintao1224

総合スコア155

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

0グッド

0クリップ

投稿2017/07/30 02:34

編集2017/07/30 09:26

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

vb.net

1 Dim cn As New NpgsqlConnection 2 Dim scmd As New NpgsqlCommand 3 Dim icmd As New NpgsqlCommand 4 Dim dr As NpgsqlDataReader 5 Dim trn As NpgsqlTransaction 6 Dim intCount As Int32 7 Dim strSSQL As String 8 Dim strISQL As String 9 Dim strDateTime As String 10 Try 11 cn.ConnectionString = 接続文字列 12 cn.Open() 13 trn = cn.BeginTransaction() 14 strSSQL = セレクト文 15 scmd.Connection = cn 16 scmd.CommandText = strSSQL 17 dr = scmd.ExecuteReader() 18 If (dr.HasRows = False) Then 19 strISQL = インサート文 20 icmd.CommandText = strISQL 21 intCount = icmd.ExecuteNonQuery 22 trn.Commit() 23 Else 24 trn.Rollback() 25 End If 26 Catch ex As Exception 27 trn.Rollback() 28 strDateTime = Format(Now, "yyyy-MM-dd hh:mm:ss") 29 Call sWriteErrorLog(strDateTime, "DB選択", ex.ToString) 30 Finally 31 cn.Close() 32 dr = Nothing 33 scmd = Nothing 34 icmd = Nothing 35 cn = Nothing 36 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で実行して問題なく実行できます。

よろしくお願いします。

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

vb.net

1 Using cn As NpgsqlConnection = New NpgsqlConnection() 2 cn.ConnectionString = 接続文字列 3 cn.Open() 4 Dim strSelectSQL As String = セレクト文 5 Dim nSelectCmd As NpgsqlCommand = New NpgsqlCommand(strSelectSQL, cn) 6 Dim dr As NpgsqlDataReader = nSelectCmd.ExecuteReader 7 If (dr.HasRows = False) Then 8 'Dim trn As NpgsqlTransaction = cn.BeginTransaction() 9 Try 10 Dim strInsertSQL As String = インサート文 11 Dim nInsertCmd As NpgsqlCommand = New NpgsqlCommand(strInsertSQL, cn) 12 Dim intCount As Int32 = nInsertCmd.ExecuteNonQuery() 13 'trn.Commit() 14 nInsertCmd.Dispose() 15 Catch ex As Exception 16 'trn.Rollback() 17 Dim strDateTime As String = Format(Now, "yyyy-MM-dd hh:mm:ss") 18 Call sWriteErrorLog(strDateTime, "DB選択", ex.ToString) 19 Finally 20 dr.Dispose() 21 nSelectCmd.Dispose() 22 End Try 23 End If 24 End Using

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

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

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

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

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

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

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

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

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

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

guest

回答2

0

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

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

sql

1INSERT INTO example_table 2 (id, name) 3SELECT 1, 'John' 4WHERE 5 NOT EXISTS ( 6 SELECT id FROM example_table WHERE id = 1 7 );

Postgres: INSERT if does not exist already

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

投稿2017/07/30 12:39

ebyhr

総合スコア16

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

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

chintao1224

2017/08/16 03:09

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

0

ベストアンサー

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

まず、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 06:50

Tak1016

総合スコア1408

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

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

chintao1224

2017/07/30 09:27

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

2017/07/30 10:30

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

2017/07/30 10:38

存在チェックのためのSQLであれば select count(*) from table where 条件 ですよね。 ExecuteReaderではなくてExecuteScalarでカウントを取得すればいいのでは? 勘だけどReaderをDisposeしてないのにInsertしてるから怒られてるんじゃないの? あとFinallyでDisposeするのはかなり古いです。Connection同様全部Usingで囲ってください。
chintao1224

2017/08/16 03:07

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問