PHPのODBC接続でトランザクションの競合が発生する問題について

このQ&Aのポイント
  • Web上からPHPへPOSTし、SQLServerへODBC関数を使用して接続しています。
  • odbc_autocommitを使用してトランザクションをかけていますが、同時にDBへの登録をすると競合してしまいます。
  • PHPのODBC接続で更新中にトランザクション(ロック)をかけることはできないのでしょうか?
回答を見る
  • ベストアンサー

SQLServerへodbc接続トランザクション

PHP 5.6.3 SQLServer 2014 IIS 8.5 Web上からPHPへPOSTし、SQLServerへODBC関数(http://php.net/manual/ja/ref.uodbc.php)を使用して接続しています。 その際「odbc_autocommit」を使用しトランザクションをかけているのですが、 同時にDBへ登録(Insert、Delete)をしたときに競合してしまいました。 そもそも「odbc_autocommit」の挙動として、 (1)A端末からDBへ接続 (2)A端末から「odbc_autocommit」を使用しトランザクション (3)B端末からDBへ接続 (4)B端末から「odbc_autocommit」を使用しトランザクション (5)A端末からTEST_TABLEへInsert (6)B端末からTEST_TABLEへInsert 上記手順の(4)の動作の際に、A端末の操作が終わるまでBは待たされるorエラーでかえると想定していたのですが・・・ ----------------------------------------------------------- $serverCon = odbc_connect($serverName,$userName,$password); if($serverCon === false){ $strMsg = ERRMSG_DBCONNECT; writeLog($strMsg, DEBUG_LOG); jumpSystemErrPage(); exit(); } $boCon = true; // 自動コミットをさせないようにする $a = odbc_autocommit($serverCon,false); if($a === false){ $strMsg = ERRMSG_DBCONNECT; writeLog($strMsg, DEBUG_LOG); $aaaa = odbc_errormsg($serverCon); writeLog($aaaa, DEBUG_LOG); jumpSystemErrPage(); exit(); } ----------------------------------------------------------- 確認方法として A端末にてデバッグ状態から上記ソース上の if($a === false){ の行でブレイクしてる最中に B端末から普通に実行した際に $aがfalseのルートに通らずそのまま次の処理へ行ってしまいました。 PHPのODBC接続で更新中にトランザクション(ロック)をかけるのはできないのでしょうか? それとも自分のやり方が間違っているのでしょうか? よろしくお願いします。

  • PHP
  • 回答数2
  • ありがとう数1

質問者が選んだベストアンサー

  • ベストアンサー
  • agunuz
  • ベストアンサー率65% (288/438)
回答No.2

「トランザクション」の意味を取り違えていませんか?(COMMITするまで)ROLLBACK可能ではありますが、COMMITしようがROLLBACKしようが無関係なものには何の影響も与えません。レコードをinsertするようなSQL文が待たされたりはしないです。 #でないと大量のデータを処理することが出来ません 手順自体はともかく、同一レコードにアクセスする(更新してcommit前のレコードを他で参照しようとする)ような「最低でもレコードロックしないといけない」SQL文で試しましょうって話です。insertのようなロックする必要がないSQL文でいちいちロック(しかもテーブルロック)するようなRDBMSはちょっと考えられません。 というか >odbc_autocommitを使わず、自分で「BEGIN TRANSACTION」の >SQL分を実行してその後Insert等の処理を行うということでしょうか? 最初の説明では >(4)B端末から「odbc_autocommit」を使用しトランザクション >(5)A端末からTEST_TABLEへInsert 明示的にトランザクションを開始しているように読めましたけど・・ 一般的には ・auto_commitが有効 BEGIN TRANSACTIONがない場合には、SQL文発行ごとにCOMMITするのと同じ(ROLLBACK不可)。 BEGIN TRANSACTIONで明示的にトランザクションを開始したら、COMMIT/ROLLBACKできる。 ・auto_commitが無効 BEGIN TRANSACTIONがない場合でも、SQL文発行でBEGIN TRANSACTIONが自動的に行われる(COMMIT/ROLLBACKできる)。 BEGIN TRANSACTIONで明示的にトランザクションを開始してもいい。 だと思います。

tyabu91
質問者

お礼

Updateを実行する際に必要なレコードに対して WITH (UPDLOCK,ROWLOCK)を指定して、処理を待つようにしました。 ありがとうございました

その他の回答 (1)

  • agunuz
  • ベストアンサー率65% (288/438)
回答No.1

試している処理はロックされていないとマズイような処理なんですかね? SQLServerやそのODBCドライバには詳しくないですが、一般的にinsertあたりはテーブルロックはしません。rollbackしたときに自分が書き込もうとしていたレコードだけ取り消せばいいしauto_increment(synonym)が空き番になるのは「困ることではない」ので。 RDBMSとSQL文によっては更新クエリでもテーブルロックしない(テーブルロックでなくレコードロックする)こともありますから、(同一レコードに対する更新クエリのような)commmit/rollbackが確定するまで待たないといけないようなSQL文で試さないと何とも言えません

tyabu91
質問者

補足

複数ユーザーが同時にアクセスする可能性があるため、トランザクションをかけたいと思っています。 例としてInsertを使いましたが、DeleteやUpdateも行われます。 >SQL文で試さないと何とも言えません odbc_autocommitを使わず、自分で「BEGIN TRANSACTION」のSQL分を実行してその後Insert等の処理を行うということでしょうか?

関連するQ&A

  • SQLserver2000のレプリケーションについて

    トランザクションとマージの違いが 知りたいです。 今、AとBの2台同じ機器構成のサーバが あってSQLServer2000 SP3a が入っていて、 AとBは同じDB状態でスタート。 Aだけが日中DBが変更され、 夜間にAの差分DBをBに適用する必要があります。 単純に考えてトランザクションパブリケーション というものを使えばよいのかなと 考えていたのですが、リアルに更新する必要は ないため、マージ?なんとかでも よいというような記述をみかけました。 初心者なものでうまく伝えれませんが、 この場合、トランザクションとマージは どちらが適しているのでしょうか。

  • SQLServer2005 リンクサーバーのトランザクションについて

    [OSのVER]:Windows Server 2003 SP2 (x64) [SQLServerのVER]:2005 Standard Edition SP3 お世話になります。トーシロです。 以下の現象で解決方法がわからず悩んでいます。 解決方法がございましたご教授頂けますでしょうか。 よろしくお願い致します。 状況: ・インスタンスAとインスタンスBがあります。 ・インスタンスAにリンクサーバーとしてインスタンスBを登録してあります。 ・インスタンスA.テーブルaにJDBC経由でデータをインサートします。 ・インスタンスA.テーブルaインサートトリガーXにて、 インスタンスB.テーブルbにデータをインサートします。 ・トリガーXはT-SQLで記述しています。 ・トリガーX内にBEGIN TRANSACTION、COMMIT TRANSACTIONを記述しています。 (記述しないとBEGINとCOMMITの対応数が違うという内容のエラーとなりました。) ・JDBC経由でのデータのインサートは一連のトランザクション内となります。 JDBC実装内容: 1.setAutoCommit(false) ↓ 2.executeUpdate()で複数回のインサート ↓ 3.commit() ↓ 4.例外発生時はrollback() ・ユーザはインスタンスA、インスタンスB共に「sa」を使用しています。 ・MSDTCは「開始」となっています。 上記状況で以下の操作をします。 1件目をインスタンスA.テーブルaにインサート→正常終了するデータをトリガーXにてインスタンスB.テーブルbにインサートします。 2件目をインスタンスA.テーブルaにインサート→制約違反が起きるデータをトリガーXにてインスタンスB.テーブルbにインサートします。 この際にエラーが発生しますが、インスタンスB.テーブルbの1件目のデータがロールバックされず、コミットされてしまいます。 インスタンスB.テーブルbの1件目のデータがコミットされないようにするには、どのような実装が必要になるのでしょうか。 (同一インスタンス内のテーブルではロールバックされます。) 以下実行時エラーです。 2パターンがランダムに出力されました。 パターン1: Error Code:1206 SQL State:S000118 com.microsoft.sqlserver.jdbc.SQLServerException: Microsoft 分散トランザクション コーディネータ (MS DTC) により、この分散トランザクションがキャンセルされました。 at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(Unknown Source) at com.microsoft.sqlserver.jdbc.IOBuffer.processPackets(Unknown Source) at com.microsoft.sqlserver.jdbc.SQLServerStatement.sendExecute(Unknown Source) at com.microsoft.sqlserver.jdbc.SQLServerStatement.doExecuteUpdate(Unknown Source) at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.executeUpdate(Unknown Source) at TestInsert.write(TestInsert.java:86) at TestInsert.main(TestInsert.java:43) パターン2: Error Code:3971 SQL State:S0001 com.microsoft.sqlserver.jdbc.SQLServerException: サーバーはトランザクションを再開できませんでした。説明: 3400000002。 at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(Unknown Source) at com.microsoft.sqlserver.jdbc.IOBuffer.processPackets(Unknown Source) at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectionCommand(Unknown Source) at com.microsoft.sqlserver.jdbc.SQLServerConnection.sendRollback(Unknown Source) at com.microsoft.sqlserver.jdbc.SQLServerConnection.rollback(Unknown Source) at TestInsert.write(TestInsert.java:96) at TestInsert.main(TestInsert.java:43) 以上、よろしくお願い致します。

  • トランザクションのネストについて

    トランザクションのネストについて お世話になります。 今、ストアドの中でストアドを実行するようなSQLを作成しているのですが、 このときのトランザクション処理について教えていただきたいです。 簡単な流れとしましては Aトランザクション開始(大枠のストアド) ↓ 処理a  ↓  Bトランザクション開始  ↓  (Aストアドの中のストアド実行)  ↓  Bコミット  ↓ 処理b ↓ Aコミット といった感じなのですが、BストアドでコミットするとBストアドをCALLする前の処理aが コミットされてしまいます。 ここはBはBだけでコミットされてほしいのです。 独自で調べた限りでは、「トランザクションのネストはMySQLではできない」ということらしいのですが、 こういった場合、どのようにしたらよいのでしょうか。 もし方法があれば、ご教示のほどお願い致します。 -環境- [DB MySQL 5.0] [OS Windows XP]

    • ベストアンサー
    • MySQL
  • insert1つの処理でもトランザクションは必要?

    お世話になります。 現在、MYSQLデータベースを使用したプログラムを書いており、 そこでトランザクションについて質問があります。 トランザクションとは、複数の処理がすべて成功した場合に正式な処理を実行(commit)、1つでも失敗した場合は元に戻す(rollback)というようなことかと思うのですが、 では、1つの処理のみの場合は、トランザクションを使用する必要はないのでしょうか? 例えば、 ・あるテーブルにデータをinsertしたい。 このような単一の処理を書く場合でも、 以下のようにトランザクションを使うべきでしょうか? $dsn = 'mysql:dbname=〇〇〇;host=〇〇〇;charset=utf8'; $user = 'user'; $pwd = 'pwd'; //DB接続 try { $pdo = new PDO($dsn, $user, $pwd); } catch (PDOException $e) { die('DB接続失敗'); } //トランザクション開始 $pdo->beginTransaction(); //INSERT try { $sql = 'INSERT into table (test1, test2, test3) VALUES (:a, :b, :c)'; $st= $pdo->prepare($sql); $ret = $st->execute(array( ':a' => $a, ':b' => $b, ':c' => $c, )); if (!$ret) { throw new Exception('INSERT 失敗'); } //commit $pdo->commit(); } catch (PDOException $e) { //rollback $pdo->rollBack(); } $pdo = null; ※前提として、テーブルを使用するユーザーは多数います。 ご存知の方、ご回答いただけるれば幸いです。 よろしくお願い致します。

    • ベストアンサー
    • PHP
  • トランザクション中に別のトランザクションは開始できますか?

    DBはサイベースを使っています。初心者です。 以下のような動作をしているシステムの一部を改造することになりました。 1)カーソルAオープン 2)トランザクション開始 3)フェッチ 4)1トランザクションでの処理数分終了済みでなければ、3)から繰り返し 5)commit/rollback 6)終了済みでなければ 2)から繰り返し 7)カーソルAクローズ 今回、3)でフェッチしたレコードのある項目をキーとして 別のテーブルを検索しなければならないのですが、 3)の後に、カーソルBオープンとフェッチのみを追加しただけでは問題があると思うのですが、同一プロセスから、トランザクション中に別のトランザクションを開始することは可能なのでしょうか? なお、このトランザクションでは、レコードの追加や削除は行いません。 どうぞよろしくお願いいたします。

  • (Windows7)VB6でODBC接続エラー

    動作環境 OS:Windows7(64bit) DB:SQL2008 Express(SP1) VB6ランタイム適用済 お世話になります。 自作のVB6EXEをWindows7で実行したところ、下記のエラーが発生してしまいます。 ErrNo:-2147168242 メッセージ:アクティブなトランザクションがありません。 ソース上のどこで発生しているのか調べたところDBのCommit時に発生 していました。 ソース:  BeginTrans  Insert文  CommitTrans  ←ここでエラー エラーにはなるのですが、Insert文の内容はDBに反映されます。 また、BeginTrans、CommitTransを使用せず、Update、Insertのみを実行した 場合はエラーは発生しません。(DBにも値が登録されます) 現状、RollbackTransができない状態です。 以下は試してみたがダメだった内容です。  ・XPやVistaでは正常に動作していたので、互換モードをVistaに設定して実行  ・Windows7(32bit)で実行 調べていて、ODBCはデフォルトで自動コミットするという記載を見かけたのですが、 DB接続時に何かしらプロパティの設定が必要なのでしょうか? 上記エラーの回避策をご存知の方がいらっしゃいましたら、ご教授願います。

  • 複数のSQL文を一つのトランザクションで行いたい(SQL Server)

    Visual Basic 2005 から SQL Server に接続してINSERTやUPDATE処理を行いたいのですが、トランザクション処理の記述方法が分かりません。 コネクションオープン トランザクション開始  クラスA呼び出し(INSERTやUPDATEの処理記述)  クラスB呼び出し(INSERTやUPDATEの処理記述)  … コミット(またはロールバック) トランザクション終了 コネクションクローズ 上記の様に、いくつかのクラスに分けて記述したINSERTやUPDATEの処理を、一つのトランザクションで行いたいです。 コネクションやトランザクションを引数とすればよいのでしょうか?

  • トランザクションについて

    トランザクションについての質問なのですが、下にある1のSQLを実行すると 'a'だけ登録されるのは、分かるのですが、 2のSQLを実行すると、'c'と'd'の両方が登録されてしまいます。 私的には、両方登録されないのかなぁと思っていたのですが・・・ COMMITが来た時点で、START TRANSACTIONの開始位置は、 あまり関係ないということなのでしょうか? よろしくお願いします。 1. START TRANSACTION; insert into test values('a'); START TRANSACTION; insert into mtuser values('b'); ROLLBACK; COMMIT; 2. START TRANSACTION; insert into test values('c'); START TRANSACTION; insert into mtuser values('d'); COMMIT; ROLLBACK; 環境:Mysql4.1.19

    • ベストアンサー
    • MySQL
  • MySQLのトランザクションについて

    WEBアプリ上の、MySQLのトランザクションについて質問です。 トランザクションは、データの挿入に矛盾がない場合commitすることによって データの挿入等を確定する機構だとおもいますが、 この仕組みって、トランザクション中は作業対象のテーブルは実行中のクライアント以外 アクセスできなくなる(※つまりロック?がかかっている?)のでしょうか? そうじゃないと、意味がないですよね?やっぱり。。。 また仮にですが、例えばAというクライアントがWEBサイトにアクセス中に Bというクライアントが待ったく 同じ動作を同じタイミングでアクセスした場合ってどうなるのでしょうか? 実際、WEBアプリでそこまでの例外というか処理って行うものでしょうか? また、ちなみにMySQLの場合、 "START TRANSACTION" というSQL文だけでなく AUTOCOMMIT = 0 として、自動コミットをオフにしつつ beginという式で トランザクションを開始する方法があるようですが、実際はどちらの方が当たり前のほうほうなのでしょうか? 識者の方ご教授ください。

    • ベストアンサー
    • MySQL
  • トランザクション処理について

    質問させて下さい。 以下の処理を行なっております。 1. トランザクション処理開始 2. テーブルAからデータをDELETE 3. テーブルBへデータをINSERT 4. トランザクション処理終了 上記処理の場合の「TYPE=InnoDB」指定の仕方が不安です。 現在はロールバックの可能性のあるテーブルAのみ「TYPE=InnoDB」を指定しています。 その状態でコミットもうまくいっているのですが、テーブルBに「TYPE=InnoDB」を 指定しなくてもよいものなのでしょうか。 環境 MySql 4.0.24

    • ベストアンサー
    • MySQL

専門家に質問してみよう