見出し画像

SELECT文の処理性能向上 5つの基本



SELECT文の処理性能を向上させるための5つの基本とは?

SQL処理性能を向上させる多くのヒントが、次のSAP社記事にまとめられています。

 <SAP社記事>
  パフォーマンスに関する注記(SAP ライブラリ)

この記事の「5 つのルール」のうち、ABAP固有ルールを除いた部分を私は「5つの基本」としました。また、各基本の表現は、同ルールのままとしました。

  1. 結果セットを小さく保つ

  2. 転送するデータ量を最小限にする

  3. データ転送数を最小限にする

  4. 検索オーバーヘッドを最小限にする

  5. データベース負荷を軽減する

前提知識

図Aは、SQLを処理するDBサーバと、プログラムが動くアプリケーション・サーバ(以下、APサーバ)の関係を表しています。なお、プログラムが発行したSQL文の流れは割愛しています。

図A:DBテーブル、DBMSおよびプログラムの関係

主な要点は、次のとおり。

  • DBテーブルの実体は、ファイル
     注:SAP HANAなどのインメモリDBの場合は、メモリ

  • 結果セット(Result Set)は、DBMS管理下のメモリ内の表

  • DBサーバとAPサーバの間で、ネットワーク通信が発生
     注:サーバの処理性能より、同通信性能は大きく劣る

  • プログラムは、SELECT句で指定した表をメモリに反映
     注:結果セットと同じ表とは限らない

なお、私以外の「結果セット」の意味として、プログラムに返される表を指す場合もあります。ご注意ください。

以下、基本1~5について説明します。

基本1.結果セットを小さく保つ

一般的な対応:ローストア型のDBMSの場合

基本1の対象は、図1-1のとおり。

図1-1:基本1の対象

結果セットを小さく保つために最もよく使うのが、WHERE句です。DBMSは、DBテーブルからWHERE句で指定した条件に合致するデータを取り出し、結果セットを作ります。

複数のDBテーブルをJOIN句で結合する場合、DBMSは図1-2のように結合した新たな結果セットを作ります。

図1-2:DBテーブル結合時の結果セット

なお、ローストア型DBMSの場合、DBテーブルから直接作られる結果セットには、DBテーブルのすべての項目が含まれます。

一般的な対応:カラムストア型DBMSの場合

カラムストア型DBMSの代表例がSAP HANAです。カラムストア型DBMSの場合、ローストア型DBMSと同じ対応の他に、SELECT句の項目を減らすことも有効です。カラムストア型の場合、カラム(列=項目)単位で処理されるためです。

例外的な対応:正規化崩し

データモデリングの基礎技法として「正規化」があります。正規化を守ってDBテーブルを実装することで多くの利点が得られます。その正規化をでは、「導出項目を持たない」が原則です。

正規化を守ったDBテーブルを使ったSELECT文の例が、次です。そのSELECT文で単価と数量から、金額を導出しています。

このSELECT文の場合、DBMSは次の①~③のように処理します。

 ① DBテーブルの全データを使って結果セットを作成
 ② ①の結果セットに金額を追加
 ③ ②の結果セットからWHERE句に該当する行の金額をプログラムに返す

SQLの処理性能を大きく悪化させる一番の原因は、上記①~②であることが多いです。なぜなら、DBテーブルのデータ量が増えるにつれて、結果セットをつくる処理量および消費メモリ量も大きくなるため。

SQLだけでは処理性能を向上させることができない場合、正規化を崩してDBテーブルに導入項目を持たせます。

図1-3:正規化崩しを含めた基本1の対象

基本2.転送するデータ量を最小限にする

一般的な対応

基本2の対象は、図2-1のとおり。

図2-1:基本2の対象

基本2は、DBサーバとAPサーバ間のネットワーク通信量を減らすこと、と言い換えられます。基本1以外に、SAP社記事にある次の3つの方針に沿って対策を考えます。

  • 行数の制限
    SELECT文のLIMIT句(※)やDISTINCTを使用する、など。
     ※ABAPの「UP TO n ROWS」はNative SQLのLIMIT句に変換されます。

  • 列数の制限
    SELECT句で必要な項目に限定する、など。

  • 集計機能の使用
    集計結果が必要な場合は、GROUP BY句を使う。プログラムで集計しない。

例外的な対応

SELECT文でデータ加工(計算や文字列操作など)してデータ転送すると、転送するデータ量が増えます。基本2に反します。

一方、基本2を守るため、プログラムでデータ加工するとプログラム処理が複雑になります。初期の実装工数が増加する、仕様追加・変更がしにくくなる、などの弊害を生みます。

どちらがいいかは、システム環境(コンピュータの性能やプログラミング言語の仕様など)やプロジェクトの都合を踏まえて決めることになると思います。

私は、特に制約がなければ、SELECT文(やDBビュー)でデータ加工する方法を採用します。なぜなら、ノーコード開発ツールやBIツールが普及しているので。プログラムではなく、SELECT文等でデータ加工したほうが統一化が図れます。

基本3.データ転送数を最小限にする

一般的な対応

基本3の対象は、図3-1のとおり。

図3-1:基本3の対象

基本3は、プログラムがSELECT文を発行する回数を減らすことを指しています。その回数を減らすため、たとえば、次のように対応します。

  • 同じようなSELECT文を一つにまとめる
    同じようなSELECT文を発行する代表例が、①ループ文の中でのSELECT文発行、②同じSELECT文を複数個所で発行。
    ①の場合、ループ文の前後で一つのSELECT文で対応するようにします。
    ②の場合、1回目のSELECT文発行で取得した値を変数に格納し、以降その変数の値を使うようにします。

  • SELECT文の結合(JOIN)や統合(UNION)、サブクエリを活用する
    結合や統合、サブクエリを使うことで、SELECT文の発行回数を削減します。
    なお、1つのSELECT文で結合等を多用すると読みにくくなる、いわゆる可読性が悪くなる可能性が高くなります。特に、サブクエリには注意が必要です。サブクエリを使う場合はWITH句を用いると可読性が良くなります。

例外的な対応

同じようなSELECT文を一つにまとめられない場合は、たとえば、次のように対応します。

  • ループ内で上記②を採用
    まず変数に該当する値があれば、その値を使います。ない場合にSELECT文を発行し、変数に格納します。

  • 上記②をクラス化
    オブジェクト指向のクラスを使えば、「変数と処理を一体化」できます。その特徴を活かし、クラスのメソッドに上記②の変数と処理を実装します。プログラムは、そのクラスをインスタンス化して使います。

【参考】クラス化の大きな利点

一般的にクラス化する大きな利点は再利用できること。複数のプログラムで同じ「変数+処理」を使えます。

また、1つのクラスを複数のインスタンスにできることも大変便利です。たとえば、得意先クラスから受注先インスタンス、出荷先インスタンスをつくって使い分けします。

クラスを使うことで、コーディング量が減り(→ 生産性向上)、バラツキも小さくなります(→ 品質向上)。積極的に活用しましょう。

基本4.検索負荷を最小限にする

一般的な対応

基本4の対象は、図4-1のとおり。

図4-1:基本4の対象

基本4を守る方法は、大きく2つに分かれます。

  • 方法1:DBインデックスを活用する
    SAP社記事も採用しています。なお、DBインデックスについては、同記事を参照してください(※)。
    ※一次索引、二次索引はSAP固有の用語ですが、どちらも一般的なDBインデックスです。一次索引はDBテーブルと一緒に実装されるDBインデックス、二次索引はその他のDBインデックスを指します。

  • 方法2:WHERE句の条件をカーディナリティが高い項目順に並べる
    まず、FROM句のDBテーブルの項目を上記順に並べます。次にJOIN句があれば同じように並べます。こうすることで、効率よく対象データを絞り込めます。

例外的な対応:正規化崩し

正規化崩しをして、上記方法1〜2が使えるようにします。詳しいことは割愛します。

基本5.データベース負荷を軽減する

一般的な対応

基本5の対象は、図5-1のとおり。

図5-1:基本5の対象

基本5は、DBMSのSQLバッファ機能を活用することを指します。DBMSがSQLバッファを使う工夫が必要です。たとえば次があります。

  • 複数のプログラムで、SELECT文を共通化を図る
    個々のソースコードにSELECT文を書くのではなく、共通プログラムやクラスを活用します。

  • 大文字の英字を使ってSELECT文を書く
    私自身は試したことはありません。複数のネット記事などで書かれていました。皆さん自身で試してみてください。

例外的な対応

基本3で説明した変数のような使い方をして、APサーバ内で完結させます。結果として、DBサーバの負荷が減ります。

ABAPの場合

ABAPプログラムは、ABAP Platform(旧SAP NetWeaver)上で動きます。ABAP Platformには専用のバッファ「テーブルバッファ」があります。これを活用することでDBサーバの負荷が減ります。詳しいことは、SAP社記事を参照してください。

図5-2:テーブルバッファが使われた場合の動き

【参考】ABAPの仕様拡張

SAP社記事が書かれたのは、2世代前のERPパッケージ(SAP R/3)の時です。今のERPパッケージはSAP S/4HANAです。SAP S/4HANAのDBMSは、SAP HANAのみです。SAP社はSAP HANAの能力を最大限活かす方針、いわゆる「コードプッシュダウン」(Code Pushdown)を謳っています。

それに合わせて、ABAP SQL(旧Open SQL)を標準SQL(のSQL92)にほぼ準拠させた仕様に拡張されています。今まで以上にSQLスキル、特にSELECT文に関するスキル向上が求められます。

ABAP SQLについて、主な仕様拡張を以下に挙げます。

  1. 計算式や関数、条件式(CASE式)の追加

  2. WITH句の追加
    注:SELECT文内に別のSELECT文を書く形式のサブクエリに関する仕様は今までどおり。この形式はFROM句やJOIN句で使えないので、WITH句で補完します。

  3. 統合(UNION、UNION ALL)の追加

また、従来の慣習も改める必要があります。たとえば、次です。

  1. 結合(JOIN)を積極的に活用する
    SAP ECCまでは、JOIN句の使用に制約がありました。具体的には、JOINできないDBテーブル(クラスタテーブルやプールテーブルなど)があったため。ABAPプログラムで結合処理せざるをえませんでした。
    SAP S/4HANAでは、すべて一般的なDBテーブル(SAP用語でいえば「透過テーブル」)になったので、上記制約がなくなりました。
     ※欄外にもう1つ補足します。

  2. カーディナリティが大きい項目順に並べる
    SAP HANAにはDBインデックスがない、と思ってよいです。そのため、基本4の方法②を採用します。
    たとえば、DBテーブル BKPF の主キー項目は「会社コード、伝票番号、会計年度」の3つです。SAP ECCまでは、WHERE句の条件を主キー項目順に並べるのが正しい考え方でした。なぜなら一次索引に合致する、つまり、基本4の方法①どおりなので。
    一方、SAP HANAにはDBインデックスがないため、「伝票番号、会社コード、会計年度」または「伝票番号、会計年度、会社コード」の順に並べるが「正しい考え方」になります。

  • 上記1の補足
     JOIN句の使用を避ける理由として、「JOIN処理は遅い」を挙げるSAPのコンサルタントや技術者が少なくありません。おそらく、SAP R/3が普及し始めた頃のDBサーバが非力だったことが起因していると思います。SAP ECCが発売された頃にはDBサーバの処理性能が向上したにも関わらず、「JOIN処理は遅いからABAPプログラムで対応する」という慣習だけが残ったと私は考えています。
     DBインデックスについての正しく学ばないまま、慣習を重視してしまった弊害かもしれません。注意しましょう。

  • 上記2の補足
     「会社コード、会計年度」と「会計年度、会社コード」のどちらかにするかは、SAP S/4HANAシステムを使う会社の数や年度の数によります。

まとめ

図Bは、図Aに基本1~5の対象を反映した状態です。SELECT文の処理性能を向上させるために、基本1~5が抜け漏れおよび重複がない状態、つまりMECEになっていることが分かります。

図B:図Aに基本1~5の対象を反映した状態

基本1~5について理解すれば、実践スキルを高められます。

いいなと思ったら応援しよう!