このページは、以下の英語ページの抄訳です。最新の情報については、英語ページを参照してください。
http://scn.sap.com/community/sql-anywhere/blog/2015/01/21/from-the-archives-mixing-sql-dialects
この記事のオリジナルは、Glenn Paulleyが sybase.com に 2009 年 5 月に掲載したものです。その中で、Glenn は SQL Anywhere における 2 つの SQL 方言(TSQL と Watcom SQL)のビルトインのサポートについて語るとともに、ストアドプロシージャー内で方言をミックスすることによる潜在的な問題について説明しています。
1995年11月、現在も Watcom SQL と呼び続けている SQL Anywhere の方言に加えて、Transact-SQL をサポートした最初のバージョンである SQL Anywhere version 5.0 をリリースしました。 (ちなみに、私が関わった SQL Anywhere の最初のバージョンが SQL Anywhere 5.0 でした。入社したのはこのリリースの1か月前でした。)この 2 つの方言の SQL 構造には、多くの共通点があります。しかしながら、重要となる大きな違いが存在するのも事実です。
この 2 つの明らかに大きな違いは、文のデリミターの使用です。
Watcom SQL では、各文を区別するのにセミコロンを使用します。一方、Adaptive Server Enterprise がサポートする Transact-SQL 方言では、 BEGIN...END
ブロック内または文の間のデリミターは特定しません。
文のデリミターがある方言とない方言の両方をサポートするのは、技術的には非常に大きなチャレンジです。SQL Anywhere サーバーは、アプリケーションが何を送るのかわからないため、どちらの方言の構造もパースできなければなりません。また、片方の方言がいつ使われ、もう一方がいつ使われるのか把握できなければならないからです。
2つの方言間の最も重要なセマンティックな違いは、おそらくエラーの処理方法にあります。SQL Anywhere のマニュアルでは、下記のように記述されています。
デフォルトのプロシージャーエラーの処理方法は、Transact-SQL 方言と Watcom 方言では大きく異なります。Watcom 方言のプロシージャーは、デフォルトでは、エラーに遭遇すると calling 環境に対して SQLSTATE と SQLCODE 値を返し、終了(exit)します。Watcom SQL ストアドプロシージャー内に EXCEPTION 文を使用して明示的なエラーハンドリングを構築することもできます。あるいは、プロシージャーに対して、エラーに遭遇した場合は ON EXCEPTION RESUME 文を使用して次の文でプロシージャーを継続するように指示することもできます。
Transact-SQL 方言のプロシージャーがエラーに遭遇した場合には、続く文でもプロシージャーは継続して実行されます。最も最新の実行文のエラー状況は、グローバル変数 @@error で保持されます。この変数は、プロシージャーからのリターンを強制する文に続いてチェックすることができます。
例えば、以下の文では、エラーが発生した場合には終了(exit)します。IF @@error != 0 RETURNプロシージャーが実行を完了すると返される値を見れば、プロシージャーが成功したのか失敗したのかがわかります。この返されたステータスは、integerで、以下のようにアクセスすることができます。
DECLARE @Status INT EXECUTE @Status = proc_sample IF @Status = 0 PRINT 'procedure succeeded' ELSE PRINT 'procedure failed'
そのため、サーバーのプロパティが、使用されている SQL 方言はどちらなのかを認識していることが重要になります。
シンタックスの糸口
インプットの際に、SQL Anywhere サーバーパーサーは、Watcom SQL バッチ、Transact-SQL バッチ、Watcom SQL 方言文 (例 CREATE PROCEDURE
)、または、Transact-SQL の方言文を予期する可能性があります。なぜならば、SQL Anywhere パーサーに対して、バッチ、プロシージャー、またはトリガーの方言が Transact-SQL であることを表す特別なシンタックス構造がいくつか存在するからです。
それには以下のものがあります。
- 以下の Transact-SQL プロシージャーにあるような、プロシージャーボディーの前の
AS
句の使用CREATE PROCEDURE showdept @deptname varchar(30) AS SELECT Employees.Surname, Employees.GivenName FROM Departments, Employees WHERE Departments.DepartmentName = @deptname AND Departments.DepartmentID = Employees.DepartmentID;
Watcom 方言のプロシージャーでは、ANSI/ISO SQL標準のとおりBEGIN...END
ブロックを使用して、プロシージャーボディーを区別します。
- トリガーにおける、trigger-time (
BEFORE
,AFTER
,INSTEAD OF
, またはRESOLVE
) の欠如。
Adaptive Server Enterprise 15.0 でサポートされる Transact-SQL 方言では、すべてのトリガーは文レベルのトリガーです。
SQL Anywhere では、そのようなトリガーは、AFTER STATEMENT
トリガーとして作成されます。
サポートされている Transact-SQL シンタックスは、以下のとおりです。CREATE TRIGGER [owner .]trigger_name ON [owner .]table_name FOR [ INSERT | UPDATE | DELETE ] AS ...
SQL Anywhere における Transact-SQL のサポートは、Adaptive Server Enterprise 15.5 のリリースで最近導入されたINSTEAD OF
トリガーは(まだ)サポートしていません。
- プロシージャー、トリガー、関数、または
SELECT
文における、SELECT
リスト表現、または変数割り当てをエイリアシング するための Transact-SQL '=' オペレーターの使用:SELECT @var = 'literal string'
Watcom SQL 方言では、SET
文を使用します。SET @var = 'literal string';
- ストアドプロシージャーへの 引数内のデフォルト値を区別するための '=' の使用。Watcom SQL 方言では
DEFAULT
句を使用します。
- ストアドプロシージャーパラメーターの仕様の後の
OUTPUT
またはOUT
の使用:CREATE PROCEDURE showdept @deptname varchar(30) OUTPUT AS ...
Watcom SQL シンタックスでは、以下のようになります。CREATE PROCEDURE showdept ( OUT @deptname varchar(30) ) BEGIN ...
- Transact-SQL 文
COMMIT TRANSACTION
、ROLLBACK TRANSACTION
、またはPREPARE TRANSACTION
の使用
逆に、特定のシンタックスがその文(単数または複数)が Watcom SQL 方言であることを見極めるインスタンスには 2種類あります。
CREATE [OR REPLACE] VARIABLE
文と
- それぞれの文を切り離すのにセミコロンが使用されている Watcom-SQL 方言
BEGIN...END
ブロックとオプショナルのラベル、変数宣言(s) 、そしてEXCEPTION
句です。
例:共通のテーブル表現
標準 SQL:2008 とSQL Anywhere では、共通テーブル表現として知られる SQL 構造をサポートしています。これは、WITH
キーワードを使用して、何が効果的なインラインのビュー定義なのかを宣言します。 WITH RECURSIVE
は、再帰クエリを再構築するのに使用されるシンタックスです。
文のデリミターを活用しない SQL 方言の中で、共通テーブル表現をサポートするのは、他の様々な SQL 構造の中で使用される WITH
キーワードのため困難です。
例えば、SQL Anywhere では、共通のテーブル表現の定義は、制約定義上の Transact-SQL WITH
句のオプショナルの使用と競合するかもしれません。
なぜならば、SQL Anywhere は、Transact-SQL プロシージャーの共通テーブル表現をサポートしていないからです。しかしながら、クエリの FROM
句 (文法のコンフリクトが問題ではないところ) に作成されたテーブル内に埋め込まれた場合には使用することができます。
余談として、Microsoft SQL Server 2008 は、オプション的に文のデリミター(セミコロン)を含む Transact-SQL プロシージャーをサポートしています。そして、Transact-SQL のオリジナルの(セミコロンでない)シンタックスに関しては、deprecate (廃止予定) です。また、Adaptive Server Enterprise とは異なり、Microsoft SQL Server は共通テーブル表現をサポートしています。しかしながら、例えば、バッチで使用される場合には、前の文は、セミコロンで終了する必要があります。もちろん、許されるのであれば、文法の競合で、LALR(1) - または、LALR(2) - パーサーの中に生成されます。
SQL Anywhere の SQL インプットのパース方法
すでに述べたとおり、SQL Anywhere は、Watcom SQL と Transact-SQL 方言の両方をサポートする必要があります。アプリケーションが何を送るのかはサーバーにはわからないため、SQL Anywhere パーサーは、パースインプットに対して複数の方法で、反復的に試みます。これは、「パースゴール」と呼ばれています(WATCOM SQL バッチ、Transact-SQL バッチ、SQL 文)。より効率性を上げるため、その接続で最後にパースに成功した文の方言を最初に試みます。
エラーがあった場合にはどうなるでしょう ? エラーの場合は、サーバーは代替のゴールを試みます。
説明すると、以下の複合文があるとします。
- begin
- declare @var int
- select @var = 100
- WITH CountEmployees( DepartmentID, n ) AS
- ( SELECT DepartmentID, COUNT( * ) AS n
- FROM Employees GROUPBY DepartmentID )
- SELECT DepartmentID, n
- FROM CountEmployees
- WHERE n <= @var
- end;
ここに、文のデリミターが欠如する Transact-SQL 複合文があります。しかしながら、サーバーは、これがa priori(演繹的、先天的)であることを知らないため、最初は単一の SQL 文としてパースを試み、失敗します。
その後、サーバーはそのブロックを Watcom SQL 方言の複合文としてパースしようとします。しかしながら、
パーサーは、DECLARE
文の最後を表すセミコロンがないため、行25から26の DECLARE @var INT SELECT
を理解できず、失敗します。
そして、Transact-SQL バッチとしてパースを試みます。しかしながら、SQL Anywhere の Transact-SQL でサポートしていない行27 の最初の共通テーブル表現が原因で、これも失敗します。
それでは、これら3つの別々の試みからどのエラーが返されるでしょうか?簡単に回答すると、「ベストなもの」になります。
SQL Anywhere で使用されているメトリックは、パーサーがより深く進めば進むほど、予測された方言によりマッチするようになります。
そのため、この例で返されるエラーは、Transact-SQL のためで、これは、"Syntax error near 'WITH' on line 4" です。
Watcom SQL の試みは、 "Syntax error near 'DECLARE' on line 3" のエラーになりました。これは、サーバーが Transact-SQL 方言を試みた後に抑制したものです。
この例におけるポイントは、SQL 方言をミックスする場合、クライアントに返されるエラーメッセージは、インプットをパースする際のサーバーによる最善の試みに基づいているということです。
そして、その結果その特定のエラーは、直観的ではないかもしれません。上の例では、 BEGIN
ブロックで使用されている共通テーブル表現ブロックper se (それ自体、本質的に)に問題はありません。単に、共通テーブル表現が Transact-SQL 方言の SQL Anywhere の実装においてサポートされていないだけです。Transact-SQL 方言が仮定されたのは、パーサーが Transact-SQL の文法を使用した複合文をとおしてさらに進めたからです。上のブロックに、セミコロンを追加していたならば、この複合文もエラーになっていたでしょう。
- BEGIN
- DECLARE @var int;
- SELECT @var = 100;
- WITH CountEmployees( DepartmentID, n ) AS
- ( SELECT DepartmentID, COUNT( * ) AS n
- FROM Employees GROUPBY DepartmentID )
- SELECT DepartmentID, n
- FROM CountEmployees
- WHERE n <= @var;
- END;
この文のデリミターは、すぐに複合文を Transact-SQL としてパースすることを終了します。しかし、行36 の変数割り当てのための Transact-SQL シンタックスの使用は、Watcom SQL 方言を使用した文のパースに相反することになります。
この場合、後のエラーが返されるエラーです。
======================
ご購入に関するお問い合わせ
こちらよりお問い合わせください。