SQLの構文についてよくある質問と、その回答について説明します。
以下の【SQL文1】と【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 AND TBL_B.基準日 = 20070430
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文において、TBL_Aに対する抽出条件指定(1)は、性能面/処理面からON探索条件とWHERE句のどちらに記述すべきでしょうか。
右側の表の条件について
以下のSQL文において、TBL_Bに対して、JOIN効率を考慮して抽出条件を記述すべきであると考えますが、OUTER JOINではON探索条件とWHERE句またはWHERE句に日付抽出条件を指定したとき(2)、JOIN結果としてのTBL_Bの日付がNULL(TBL_Bにレコードがない)の場合、レコードが抽出されないのではないでしょうか。
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)
左側の表の条件について
ON探索条件に指定する制約条件とWHERE句に指定する制約条件では、SQL文の意味が変わるため、条件をどちらに記述するかは、性能面からは決まりません。
これは、表を結合するときの条件として働きます。この条件が偽のとき、LEFT OUTER JOINでは右表の結合結果がNULLに、RIGHT OUTER JOINでは左表の結合結果がNULLになります。なお、結合結果は、LEFTなら左表のレコードのすべてを対象とした結合結果となり、RIGHTなら右表のレコードのすべてを対象とした結合結果となります。
これは、FROM句に記述される表からレコードを抽出するときの条件として働きます。(INNER)結合条件ならば、表と表をネステドループ結合(繰り込み結合)した結果表からの抽出条件として働きます。制約条件は、表からレコードを抽出するときの条件として働きます。なお、FROM句がOUTER JOINのとき、OUTER結合した結果表(結合表)からレコードを抽出するときの条件として働きます。
ON探索条件に記述すると、その条件が偽であるかにかかわらず、LEFTでは左表のレコードを抽出し、RIGHTでは右表のレコードを抽出します。したがって、求める結果が、基準日=20070430のレコードだけに限定したいときは、WHERE句の探索条件にその制約条件を記述します。
なお、ON探索条件に(1)が記述されず、WHERE句の探索条件には(1)の条件が記述されたとします。このとき、Symfoware/RDBでは、TBL_Aの表からレコードを抽出するとき(OUTER結合する前に)、(1)の条件の下でレコードを抽出します。つまり、(1)がDSIを限定する条件であれば、DSIを限定して表をアクセスすることになります。
右側の表の条件について
TBL_Bについても、ON探索条件に記述される制約条件とWHERE句の探索条件とでは意味が変わります。
上述したとおり、表と表を結合するときの条件として働きます。詳細は1.を参照してください。なお、TBL_Bの制約条件がON探索条件に記述される場合、その条件が偽のときはTBL_Bの列にはNULL値を設定するという意味であることから、その条件範囲外のレコードは結合時には必要ないという意味であり、すなわち、TBL_Bからレコードを抽出する必要がないので、検索範囲外となります。Symfoware Serverでは、その条件がDSIを限定するような条件であれば、DSI検索範囲を絞って検索しています。
上述したとおり、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文の性能について”があります。必要に応じて参照してください。
以下の条件を満たす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する場合は結合順を指定するカッコを省略することができますが、カッコを付けて記述を明瞭にすることをお勧めします。
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)
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の記述方法は?”があります。必要に応じて参照してください。
以下の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 = "INSERT INTO T_C ( C_ID, ..., ~
ENTRANCE, ... )" & _"VALUES ( " & _
...
"DATE'" & c.REENTRANCE & "'," & _
... & " ) "
Common.G_CCDB.Execute (SQL)
参照
詳細は、“SQLリファレンス”の“定数”を参照してください。
Symfoware Serverには文字列を数字に変換する関数があるか?
Symfoware Server独自にはそのような変換関数がありませんが、変換関数ではなくSQL規格にあるCASTを使うことにより、文字列型のデータを数値型のデータに変更することができます。
以下に、文字列“1234”を、数値(INTEGER型)にする場合の例を示します。
CAST(’1234’AS INTEGER)
以下に、列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型の場合、データベースへの格納時に内部的にコード変換を行います。文字や数値が混在したデータの場合、数値を文字コードとして処理して誤動作するなど、コード変換が正常に動作しないことがあります。