ページの先頭行へ戻る
Symfoware Server V10.0.0 トラブルシューティング集

8.1.3 SQLの構文について知る

SQLの構文についてよくある質問と、その回答について説明します。

SQL文の性能について

以下の【SQL文1】と【SQL文2】は、同じ性能になります。

SQL文1
SELECT .....

       FROM TBL_A,TBL_B
       WHERE TBL_A.基準日 = TBL_B.基準日
       AND TBL_A.店番 = TBL_B.店番
       AND TBL_A.取引先番号 = TBL_B.取引先番号
       AND TBL_A.基準日 = 20070430
       AND TBL_B.基準日 = 20070430
SQL文2】
SELECT .....

        FROM TBL_A,TBL_B
        WHERE TBL_A.基準日 = TBL_B.基準日
        AND TBL_A.店番 = TBL_B.店番
        AND TBL_A.取引先番号 = TBL_B.取引先番号
        AND TBL_A.基準日 = 20070430

Symfoware/RDBでは、制約条件(TBL_A.基準日=20070430)と結合条件(TBL_A.基準日=TBL_B.基準日)を使って、TBL_Bの検索範囲を狭くしています。このため、【SQL文2】は、“TBL_B.基準日=20070430”がWHERE条件に指定されたのと同じように処理されます。したがって、【SQL文1】と【SQL文2】は同じ性能になります。

なお、SQL文の見やすさの観点から【SQL文1】の記述をお勧めします。

また、関連する質問として下記の“SQL文のOUTER JOINの記述方法は?”があります。必要に応じて参照してください。

SQL文のOUTER JOINの記述方法は?

質問
  1. 左側の表の条件について
    以下のSQL文において、TBL_Aに対する抽出条件指定(1)は、性能面/処理面からON探索条件とWHERE句のどちらに記述すべきでしょうか。

  2. 右側の表の条件について
    以下のSQL文において、TBL_Bに対して、JOIN効率を考慮して抽出条件を記述すべきであると考えますが、OUTER JOINではON探索条件とWHERE句またはWHERE句に日付抽出条件を指定したとき(2)、JOIN結果としてのTBL_Bの日付がNULL(TBL_Bにレコードがない)の場合、レコードが抽出されないのではないでしょうか。

【SQL文】
SELECT .....
        FROM TBL_A LEFT OUTER JOIN TBL_B
        ON TBL_A.基準日=20070430               ...(1)
        AND TBL_A.基準日=TBL_B.基準日
        AND TBL_A.店番=TBL_B.店番
        AND TBL_A.取引先番号=TBL_B.取引先番号
        WHERE TBL_A.基準日 = 20070430          ...(1)
        AND TBL_B.基準日 = 20070430            ...(2)
【回答】
  1. 左側の表の条件について
    ON探索条件に指定する制約条件とWHERE句に指定する制約条件では、SQL文の意味が変わるため、条件をどちらに記述するかは、性能面からは決まりません。

    ON探索条件

    これは、表を結合するときの条件として働きます。この条件が偽のとき、LEFT OUTER JOINでは右表の結合結果がNULLに、RIGHT OUTER JOINでは左表の結合結果がNULLになります。なお、結合結果は、LEFTなら左表のレコードのすべてを対象とした結合結果となり、RIGHTなら右表のレコードのすべてを対象とした結合結果となります。

    WHERE句の探索条件

    これは、FROM句に記述される表からレコードを抽出するときの条件として働きます。(INNER)結合条件ならば、表と表をネステドループ結合(繰り込み結合)した結果表からの抽出条件として働きます。制約条件は、表からレコードを抽出するときの条件として働きます。なお、FROM句がOUTER JOINのとき、OUTER結合した結果表(結合表)からレコードを抽出するときの条件として働きます。

    ON探索条件に記述すると、その条件が偽であるかにかかわらず、LEFTでは左表のレコードを抽出し、RIGHTでは右表のレコードを抽出します。したがって、求める結果が、基準日=20070430のレコードだけに限定したいときは、WHERE句の探索条件にその制約条件を記述します。

    なお、ON探索条件に(1)が記述されず、WHERE句の探索条件には(1)の条件が記述されたとします。このとき、Symfoware/RDBでは、TBL_Aの表からレコードを抽出するとき(OUTER結合する前に)、(1)の条件の下でレコードを抽出します。つまり、(1)がDSIを限定する条件であれば、DSIを限定して表をアクセスすることになります。

  2. 右側の表の条件について
    TBL_Bについても、ON探索条件に記述される制約条件とWHERE句の探索条件とでは意味が変わります。

    ON探索条件

    上述したとおり、表と表を結合するときの条件として働きます。詳細は1.を参照してください。なお、TBL_Bの制約条件がON探索条件に記述される場合、その条件が偽のときはTBL_Bの列にはNULL値を設定するという意味であることから、その条件範囲外のレコードは結合時には必要ないという意味であり、すなわち、TBL_Bからレコードを抽出する必要がないので、検索範囲外となります。Symfoware Serverでは、その条件がDSIを限定するような条件であれば、DSI検索範囲を絞って検索しています。

    WHERE句の探索条件

    上述したとおり、FROM句に記述される表からレコードを抽出するときの条件として働きます。すなわち、TBL_Bの制約条件が記述される場合、それが、たとえば(2)の条件のようにNULL以外を抽出するような条件であれば、実はOUTER JOINではなく、INNER JOINという意味となります。

    Symfoware Serverでは、このようなSQL文においては、INNER JOINに変換して、アクセスプランを作成しています。たとえば、「WHERE TBL_B.店番 IS NULL、TBL_AにはあるがTBL_Bにはないもの」というような、WHERE句の条件がNULLのものを抽出するという条件のときは、OUTER JOINの必要があるため、INNER JOINへの変換を行わず、結合表からの抽出条件として条件評価しています。
    なお、そのWHERE句の条件は、TBL_Bを検索するときの(OUTER結合する前の)条件としては使えないため使っていません。
    このように、WHERE句の探索条件は、TBL_Aの条件とTBL_Bの条件とは違った意味をもっています。したがって、TBL_Bから抽出するレコードを基準日=20070430にしたい場合は、ON探索条件に記述すると性能がよくなります。ただし、ON探索条件に制約条件を記述するときは、結果が同じになるよう注意して記述する必要があります。

なお、関連する質問として上記の“SQL文の性能について”があります。必要に応じて参照してください。

INNER JOINとOUTER JOINの記述方法は?

質問

以下の条件を満たすSQL文を記述したいのですが、どう書けばいいでしょうか。

  • 個人情報については、行事に参加しているかにかかわらず、全員の情報を出力したい。なお、行事に参加していない人は、参加行事履歴に履歴情報はない。

  • 行事にない参加行事履歴はないはずだが、もしあった場合は、その参加行事履歴のレコードは無視する。

テーブルの関係は、以下のとおりです。

【回答】

SQLの概略としては、個人参加行事履歴と行事をINNER JOINし、個人情報とそのINNER JOIN結果をOUTER JOINすることになります。
SQL文の例を以下に示します。

SELECT 個人情報.顧客番号,
       DRIVE_TBL.行事年度,DRIVE_TBL.行事コード
FROM   (S2.個人情報 LEFT OUTER JOIN
         (SELECT 参加行事履歴.顧客番号,
              参加行事履歴.行事年度,
                   参加行事履歴.行事コード
              FROM S2.参加行事履歴, S2.行事
              WHERE 参加行事履歴.行事年度 = 行事.行事年度
                  AND 参加行事履歴.行事コード = 行事.行事コード)
                 AS DRIVE_TBL(顧客番号, 行事年度, 行事コード)
       ON 個人情報.顧客番号 = DRIVE_TBL.顧客番号)

3テーブル以上のOUTER JOIN場合、どのようにSQL文を記述すればいいか?

一般的に、OUTER JOINは2テーブルに対する結合を行うものです。
3つ以上のテーブルをOUTER JOINするときは、2テーブルのOUTER JOINをさらにOUTER JOINするというように記述します。このOUTER JOINの順序は、カッコを付けるなどして階層的に表現します。
なお、FROM句に記述された順でOUTER JOINする場合は結合順を指定するカッコを省略することができますが、カッコを付けて記述を明瞭にすることをお勧めします。

1
SELECT T1.C1, T2.C2, T3.C3

       FROM ((T1 LEFT OUTER JOIN T2 ON T1.C1 = T2.C1)
       LEFT OUTER JOIN T3 ON T1.C1 = T3.C1)
例2
SELECT T1.C1, T2.C2, T3.C3

       FROM T1 LEFT OUTER JOIN T2 ON T1.C1 = T2.C1
       LEFT OUTER JOIN T3 ON T1.C1 = T3.C1

参照

詳細は、“SQLリファレンス”の“DECLARE CURSOR(カーソル宣言)”を参照してください。
また、関連する質問として上記の“SQL文のOUTER JOINの記述方法は?”があります。必要に応じて参照してください。

DATE属性をもつ表への挿入方法は?

質問

以下のSQLを実行すると、「データ型が代入可能ではありません」というエラーになりました。
どう記述すればよいでしょうか。

【SQL部分】
SQL = "INSERT INTO T_C ( C_ID, ...,

                ENTRANCE, ... )" & _"VALUES ( " & _

                ...

                "'" & c.REENTRANCE & "',"& _
                         (注)
                ... & " ) "

         Common.G_CCDB.Execute (SQL)

注)VBAのDATE型の部分です。値は“2007-04-08”です。

【回答】

DATE型の定数値は、たとえば DATE'2007-4-8' のように、シングルクォートの前にDATEを付加した形式で指定してください。同じように記述するものとしては、以下の定数があります。

定数名

指定形式

各国語文字列定数

N'日本語'

日付定数

DATE'2007-04-08'

時刻定数

TIME'17:20:01'

時刻印定数

TIMESTAMP'2007-04-08 17:20:01'

時間隔定数(年-月型)

INTERVAL'1-6' YEAR TO MONTH

なお、質問のINSERT文では、以下のとおりに記述します。

【SQL部分】
SQL = "INSERT INTO T_C ( C_ID, ...,        ~

                ENTRANCE, ... )" & _"VALUES ( " & _

                ...
                "DATE'" & c.REENTRANCE & "'," & _

                ... & " ) "

          Common.G_CCDB.Execute (SQL)

参照

詳細は、“SQLリファレンス”の“定数”を参照してください。

Symfoware Serverには文字列を数字に変換する関数があか?

Symfoware Server独自にはそのような変換関数がありませんが、変換関数ではなくSQL規格にあるCASTを使うことにより、文字列型のデータを数値型のデータに変更することができます。

例1

以下に、文字列“1234”を、数値(INTEGER型)にする場合の例を示します。

CAST(’1234’AS INTEGER)
例2

以下に、列COL01の値を、数値(DECIMAL(10, 3))にする場合の例を示します。

CAST(COL01 AS DECIMAL(10, 3))

参照

詳細は、“SQLリファレンス”の“CAST指定”を参照してください。

ロシジャ定義する変数の精度は指定する必要があるか?

指定する必要があります。

DECLARE A VARCHAR(10) ;
DECLARE B CHAR(10) ;
DECLARE C DECIMAL(10,3) ;
DECLARE D NUMERIC(10,3) ;

なお、関連する質問として“1.2.1 プロシジャ定義時の異常”の“定義文を入力してrdbddlexコマンドを実行すると、qdg12226eのエラーが発生する”があります。必要に応じて参照してください。

DECIMALとNUMERICの使い分け、性能の概要について知りたい

機能、性能についてはどちらを使用しても違いはありません。
アプリケーションの論理に従って使い分けてください。

ただし、DECIMAL属性の列とNUMERIC属性の列を算術演算した場合、結果のデータ属性は DECIMALとなります。
また、C言語アプリケーション、Java言語アプリケーション、.NETアプリケーションで利用する場合、アプリケーションに取り出した際にfloat型などに変換され、桁落ちする場合があります。

参照

詳細は、“SQLリファレンス”の“数値式”を参照してください。

時間隔型「INTERVAL」の使いかたについて知りたい

INTERVAL型は、2つの時刻または日時の間隔を取り扱うためのデータ型です。

たとえば、列『発注日』と『納品日』を使って、納品までの経過日数を求めることができます。

SELECT 発注番号, (納品日-発注日)DAY(3)
FROM 取引.発注表
WHERE 発注日 >= DATE'2007-04-01' AND 納品フラグ = '1'
ORDER BY 2 DESC ;

参照

詳細は、“SQLリファレンス”の“時間隔値式”を参照してください。

記述可能なSQL文の最大文字長は?

SQL文の文字列長に制限はありません。
ただし、動的SQL文を利用する場合には、最大文字列長は32000バイトとなります。
また製品バージョン、プラットフォームによる違いはありません。

参照

詳細は、“アプリケーション開発ガイド(共通編)”の“定量制限”を参照してください。

BLOB型にデータを格納するときに、“項目名_reservedには何を設定するのか?

設定する必要はありません。
area_reservedは将来の機能拡張のための予約域であり、現在は何も設定する必要はありません。

ポイント

_reservedについて

たとえば、C言語の埋込みSQLアプリケーションで、以下のようにホスト変数を定義します。

SQL TYPE IS BLOB (1K)area;

この場合、コンパイル時に上記のホスト変数定義は、以下の構造体に変換されます。

struct{
        long          area_reserved;
        unsigned long area_length;
        char          area_data[1024];
}area;

文字と数値の混在したデータをCHAR型に格納できか?

CHAR型には格納できません。

文字や数値の混在したデータや、画像データなどのマルチメディア・データを格納するデータ型には、BLOB型を使用してください。

CHAR型の場合、データベースへの格納時に内部的にコード変換を行います。文字や数値が混在したデータの場合、数値を文字コードとして処理して誤動作するなど、コード変換が正常に動作しないことがあります。