このページは、以下の英語ページの抄訳です。最新の情報については、英語ページを参照してください。

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 TRANSACTIONROLLBACK 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 文)。より効率性を上げるため、その接続で最後にパースに成功した文の方言を最初に試みます。

エラーがあった場合にはどうなるでしょう ? エラーの場合は、サーバーは代替のゴールを試みます。

説明すると、以下の複合文があるとします。

 

  1. begin
  2.   declare @var int
  3.   select @var = 100
  4.   WITH CountEmployees( DepartmentID, n ) AS
  5.   ( SELECT DepartmentID, COUNT( * ) AS n
  6.     FROM Employees GROUP BY DepartmentID )
  7.   SELECT DepartmentID, n
  8.   FROM CountEmployees
  9.   WHERE n <= @var
  10. 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 の文法を使用した複合文をとおしてさらに進めたからです。上のブロックに、セミコロンを追加していたならば、この複合文もエラーになっていたでしょう。

  1. BEGIN
  2.   DECLARE @var int;
  3.   SELECT @var = 100;
  4.   WITH CountEmployees( DepartmentID, n ) AS
  5.   ( SELECT DepartmentID, COUNT( * ) AS n
  6.     FROM Employees GROUP BY DepartmentID )
  7.   SELECT DepartmentID, n
  8.   FROM CountEmployees
  9.   WHERE n <= @var;
  10. END;

この文のデリミターは、すぐに複合文を Transact-SQL としてパースすることを終了します。しかし、行36 の変数割り当てのための Transact-SQL シンタックスの使用は、Watcom SQL 方言を使用した文のパースに相反することになります。

この場合、後のエラーが返されるエラーです。

===

 

SAP SQL Anywhere に関する詳細情報は、SAP SQL Anywhere Communityページ<英語> を参照してください。

 

上記のコミュニティーに掲載されている技術情報は、順次SQL Anywhere 日本語コミュニティ

に掲載しています。

 

SQL Anywhere に関してはまずはこちらをご参照ください。無期限でご利用いただける無償の Developers Edition もこちらからダウンロードが可能です。

 

SQL Anywhere に関して技術的な質問のある方はコミュニティに登録し、
「+ Actions」から「Ask a Question」機能をご利用ください。

Language には「Japanese」、
Primary Tag には「SQL Anywhere」、
Additional tag には「SAP SQL Anywhere」、
User Tagに「sql anywhere japanese question」

を選択してください。

不具合につきましては、サポート契約者様専用の問い合わせ方法にてお問い合わせください。

 

======================
ご購入に関するお問い合わせ

こちらよりお問い合わせください。

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply