現在作っているシステムでは、ビジネスロジックは、大部分をストアドプロシージャでコーディングしています。
通常のSQLからの利用や、SSIS経由のジョブからの実行、というだけでなく、SOAP化してSQL Server外からも実行するのが簡単で、再利用しやすいからです。(IISやNative XML Webサービスを使って。)
さて、そうこう進めているうちに、「1つのストアドの実行後、それだけで終わらず、処理結果を他のシステムと渡さないといけない」といった、状況が増えてきました。ビジネスロジックをアプリケーションサーバで組んでいれば、他システムとの連携はやりやすかったのかもしれませんが、上記の通り実装はストアドが中心。
処理結果を連携させる必要のある、もう一つのシステムにも、本来はDBの外から連携させた方が良いのですが、能力と手間の問題で、ストアドからストアドの呼び出し、という形で連携させてしまえば良いかな、と安易に考えてしまいました。(もう一つのシステムのデータソースも、同じSQL Serverのインスタンス上にあるので...)
ところが、これが大きな落とし穴になってしまいました(T_T)
「こっちのストアドから、そっちのストアドを呼び出せば良いのよね」と、もう1つのシステムの担当者と軽く話を進めたのですが、表面上は1つづつしかネストしないと思っていたストアド、お互い、既にその自身内に、いろんなストアドや関数をネストしていました。
また、ストアドの結果を受けて処理を分岐させるため、INSERT...EXECで結果を受け取っていたのですが、自分の担当分では正常に処理が終了するのに、さらに相手に自分の実装したストアドを実行してもらうと、以下のようなエラーが発生してしまい、正常に実行できません!
INSERT-EXEC ステートメント内では ROLLBACK ステートメントを使用できません。
また、それぞれのストアドには、Transaction処理(エラーが発生したら、Rollbackする)も設けていたのですが、何度修正しても、以下のようなエラーが発生してしまいました...。
EXECUTE 後のトランザクション数は、 COMMIT TRAN または ROLLBACK TRAN ステートメントに間違いがあることを示しています。以前の数 = 1、現在の数 = 0 です。
せっかく再利用できると思ってストアドを多用したのに、ほとんど同じコードを組み込み直さないといけないのかしら...と、だいぶ辟易していたころに、こんなサイトを見つけました。
[ How to Share Data Between Stored Procedures ] http://www.sommarskog.se/share_data.html#INSERTEXEC
ここに、以下のような一文が。
It can't nest. If some_sp tries to call some_other_sp with INSERT-EXEC, you will get an error message. Thus, you can only have one INSERT-EXEC active at a time. This is a restriction in SQL Server.
いつも参考にしていた本には、ストアドのネストの場合は、INSERT-EXECで結果を受け取れば良いという旨しか書かれていなかったので、こういう制限があるとは、分りませんでした....。(実際に、これって本当なんでしょうか??)
また、ロールバックのエラーについては、こちらを呼んで、なんとか納得いたしました...。
[ Handling SQL Server Errors in Nested Procedures ] http://www.vfpconversion.com/Article.aspx?quickid=0305111
以前、アプリケーションサーバ上(J2EE/EJB)で、ビジネスロジックを組んだことがあるのですが、その時は、Commin/Rollback処理を含んだ1つのビジネスロジックを、さらに別のCommin/Rollback処理を含むビジネスロジックでラップした場合、トランザクションマネージャがうまく処理をしてくれていました。(一番上のビジネスロジックのトランザクションに合わせる)
そこでの理解が足りなかったのかもしれませんが、ストアドプロシージャもそういうことが可能と思って、安易にネストさせてしまっていました。これがいけないんでしょうね...。
また、本当は、他システムとの連携は、BASやらService Brokerを使ったりするほうが望ましいのですよね...。