- ベストアンサー
SQL Server(MSDE2000)でのALTER TABLEでの列追加とUPDATEの問題
- SQL Server(MSDE2000)において、ALTER TABLEで列を追加した後に直後のUPDATEで値をセットできない問題が発生しています。UPDATEに失敗するだけでなく、変更が反映されない状況に陥ります。
- 一度のSQL文の実行中にALTER TABLEで新設した列に対しては、すぐにはアクセスできないトランザクション的な挙動が起こっている可能性があります。
- 回避策としては、SQL文を個別に作成し、個別に実行する方法がありますが、できれば一つのSQL文にまとめたいと考えています。ある方は、この現象の原因や理由について情報を持っている方はいませんか?
- みんなの回答 (1)
- 専門家の回答
質問者が選んだベストアンサー
SQL文を実行するときには、まずコンパイルして、実行プランを立て、それから実行するというステップがあるという点をご理解ください。 私の理解ではその時点でTEST_TABLEが存在しないかぎり、CREATE~INSERT~ALTER~UPDATEはエラーにならないはずです。 ただし、"既にTEST_TABLE(ALTER前)がある状態"で4行一緒に実行すると、 列名 'TEST_FIELD3' は無効です。 と返されますので、それと混同されているのではないかなと推測します。 (CREATE TABLE文は既にテーブルが存在していても、コンパイル時にはエラーにはならず、実行時にエラーになるため。 一方、UPDATE文は後述の通り、コンパイル時にエラーになります) CREATE~INSERTが成功したあとで、ALTER~UPDATEが失敗するのは、実行に失敗しているのではなく、コンパイルに失敗しているということです。 (「実行して失敗し、ロールバックされた」わけではありません) 既にそのテーブルが存在する場合、SQL Serverはコンパイル時にテーブルのカラムをチェックします。 この時点のテーブルにはTEST_FIELD3はありませんので、コンパイルエラーになります。 一方、実行時にそのテーブルが存在しない場合は、遅延解決と言ってコンパイル時にはカラムがチェックされません。 (実行時に条件がそろっていなければエラーとなります) 従って、CREATE~INSERT~ALTER~UPDATEと一気に実行すると、チェックすべきテーブルがまだ存在していないため、エラーにはなりません。 もう一つ注意すべきなのは、キャッシュの存在です。 一度「UPDATE TEST_TABLE SET TEST_FIELD3 = 3」が成功すると、このクエリはプランキャッシュというところにキャッシュされ、必要に応じて再利用されます。 その状態でテーブルをDROPし、CREATE~INSERTを行い、ALTER~UPDATEを行うと、キャッシュされた情報を参照してTEST_FIELD3は存在すると判断され、コンパイルが通りますので、今度はエラーになりません。 ご参考までに。
お礼
早速の回答、ありがとうございました。 頂いた回答の中の「コンパイル→実行プラン立て→実行」というステップのお話からヒントを得て、問題を解決する方法が見つかりました。 まず、プランキャッシュの存在の事が全く頭に無かったため、UPDATE を実行する時点で、オプティマイザがまだ「TEST_FIELD3」の存在に気が付いていないため(直前で ALTER TABLE で追加される事なんて、コンパイル時点でオプティマイザが知る由もありませんよね)、当たり前のようにエラーになる事に気が付いていませんでした。 しかも、完全に DROP TABLE した状態からでは、提示した4行の実行ではエラーが発生せず、コンパイルが通ってしまう事も、当方のテスト不足で、気が付いていませんでした。 ご指摘を頂いたポイントを参考にし、解決方法を模索した結果、以下のような方法で、当方の問題が解決できた事をご報告します。 まず、 -------- CREATE TABLE TEST_TABLE ( TEST_FIELD1 SMALLINT, TEST_FIELD2 SMALLINT) INSERT INTO TEST_TABLE VALUES( 1, 2 ) -------- を先に実行しておいた状態で、 -------- DECLARE @SQLSTR VARCHAR(64) SET @SQLSTR = 'UPDATE TEST_TABLE SET TEST_FIELD3 = 3' ALTER TABLE TEST_TABLE ADD TEST_FIELD3 SMALLINT EXEC (@SQLSTR) -------- というSQL文を実行します。 もうお気付きかと思いますが、コンパイル時にエラーとなってしまっていた UPDATE 文を、文中で動的に組み立てた事にしてしまい、EXECで実行した訳です。 「動的に組み立てられたSQLを実行する際は、実行時にコンパイルされる」という特性を利用してみました。 これによって、当方の問題を解決する事ができました。 ご回答頂いた内容が、かなりのヒントになりました。非常に助かりました。 それと、今回の指摘を受けて思った事は、プランキャッシュの影響を受けるようなSQL文をデバッグする際には、実行毎に、SQLServerのサービスを停止・再開させて強制的にプランキャッシュを破棄した方が、実行結果がプランキャッシュの影響を受けずに済むという点です。ここについては、今後も注意したいと思います。 当然ながら、ベストアンサーとさせて頂きます。 ありがとうございました!