株式会社ジーワンシステムの代表取締役。 新しいものを生み出して世の中をあっといわせたい。イノベーションってやつ起こせたらいいな。

SQL ServerとOracleの一番大きな違い

»

 ぶっちゃけて、SQL Serverも、Oracleも、フリーのPostgreSQLも、RDBMSとしての本質的な性能差は本当に小さい。そのためRDBMSのベンダーを選択するのは、バックアップとか、レプリケーションとか、クラスタリングなどの機能や関連ツールのできと、IDEとの相性などを検討するのが良いでしょう。

 しかし、一般的にあまり検討の対象になってない部分が、実は一番大きな違いだったりします。わたしは、SQL ServerとOracleの差で、もっとも大きいのはストアドプログラム(プロシージャ・ファンクション)の書き方(構文)の違いだと思います。

 単純なロジックはOracleの方が書きやすい。SQL ServerのIfなどのステートメントブロックにBEGIN ~ END って書かないといけないのは、大概勘弁して欲しいと思います。それでも、わたしがSQL Serverを推しているのは、SELECT系のストアドプログラムが圧倒的に書きやすいから。逆にOracleはSELECT系のストアドプログラムが書きにくく、Oracleが圧倒的なシェアを持っていたころに経験したSEたちに、「ストアドプログラムはバッチのためにある」という誤解を持たせてしまっています。

 繰り返しますが、OracleではSELECT系のストアドプログラムは滅茶苦茶書きにくいです。具体的には、いろんな方法があると思いますが、基本的には次の3種類ではないかと思います。

  1. ワークテーブルを使う
      セッションIDをキーにしたり、夜間バッチで消したりと余計なコストが掛かる
  2. REF CURSOR データ型の引数に戻す
      フレームワークが対応していなかったりするし、テストが簡単にはできない
  3. PL/SQL表を使う
      わたしはこのパターンを推奨

 というわけで、3.で書いたとします。

■ Oracleの場合

# パッケージにしなければもう少し短いですが。

CREATE OR REPLACE PACKAGE TEST_PKG AS
/*
  * テスト作成
  */

  -- TEST_FUNC 用のオブジェクトタイプを作成
  TYPE TEST_FUNC_ROW IS RECORD
  (
    ID NUMBER  -- 主キーです
    , NAME VARCHAR2(40)  -- 名前
    , Bdate DATE  -- 誕生日
    , ADDRESS VARCHAR2(255)  -- 住所
  );

  -- TEST_FUNC 用のレコードセットを定義
  TYPE TEST_FUNC_RSET IS TABLE OF TEST_FUNC_ROW;

  /* ■□■□ TEST_FUNC
   * テスト
   * 得意先を検索する
   */
  FUNCTION TEST_FUNC
    (
    PARM1 IN NUMBER  -- 1つ目のパラメータ
    , PARM2 IN VARCHAR2  -- 2つ目のパラメータ
  )
  RETURN TEST_FUNC_RSET PIPELINED;

END;
/

CREATE OR REPLACE PACKAGE BODY TEST_PKG AS
/*
  * テスト作成
  */

  /* ■□■□ TEST_FUNC
   * テスト
   * 得意先を検索する
   */
  FUNCTION TEST_FUNC
    (
    PARM1 IN NUMBER  -- 1つ目のパラメータ
    , PARM2 IN VARCHAR2  -- 2つ目のパラメータ
  )
  RETURN TEST_FUNC_RSET PIPELINED
  IS

    CURSOR Cur_Main IS
      -- 本番時は以下のSQLを修正し、
      -- このコメントを削除する。
      SELECT * -- 解説のために * ね
      FROM xTEST_FUNC_VIEW
      WHERE
        1 = 1
        AND ID = PARM1
        AND NAME LIKE PARM2
      ;

    Row_Main Cur_Main%ROWTYPE;

    ReturnRow TEST_FUNC_ROW;

  BEGIN

    OPEN Cur_Main;
    Loop
      FETCH Cur_Main INTO Row_Main;
      EXIT WHEN Cur_Main%NOTFOUND;

      ReturnRow.ID := Row_Main.ID;  -- 主キーです
      ReturnRow.NAME := Row_Main.NAME;  -- 名前
      ReturnRow.Bdate := Row_Main.Bdate;  -- 誕生日
      ReturnRow.ADDRESS := Row_Main.ADDRESS;-- 住所

      PIPE ROW(ReturnRow);  -- データを出力する。

    END Loop;

    RETURN;

  END TEST_FUNC;

END;
/

■ SQL Serverの場合

# CREATE OR REPLACE をできるようにして欲しいな~。

/*
  * テスト作成
  */

  IF  EXISTS
      (SELECT * FROM sys.objects
      WHERE object_id = OBJECT_ID(N'[dbo].[TEST_FUNC]')
          AND type in (N'P', N'PC'))
  DROP PROCEDURE [dbo].[TEST_FUNC]
  GO
  
  /* ■□■□ TEST_FUNC
   * テスト
   */
  CREATE PROCEDURE  TEST_FUNC
    (
    @PARM1 INT  -- 1つ目のパラメータ
    , @PARM2 VARCHAR(40)  -- 2つ目のパラメータ
  )
  AS

    SET NOCOUNT ON;

    -- 本番時は以下のSQLを修正し、
    -- このコメントを削除する。
    SELECT * -- 解説のために * ね
    FROM xTEST_FUNC_VIEW
    WHERE
      1 = 1
      AND ID = @PARM1
      AND NAME LIKE @PARM2
    ;

GO

 となります。いずれも、実行時は普通のSELECT文と同じに扱えますから、この結果をO/Rマッパーなどでマッピングすると本当に工数は下がりますし(そこは否定したことはないよ)、以前から書いてきたように、画面はパーツを並べて活性非活性や色などの制御を顧客の気に入るように現物を見せながら調整する方が早いのです。つまり、アジャイル開発に向いています。

 もちろん、そこをウォーターフォールでやってもかまわないけれど、とにかく、外部(UI)と内部(DB)を明確に分けた方が工数は下がりますし、顧客満足度も上がります。パフォーマンスも良いものになります。

 本当に、SQLを書く能力はプログラミング力の中でもっとも差が強く出ますから、無理して全員がSQLをできるようするのも馬鹿げているし、できない人に合わせて全体のレベルを下げるのも間違っているでしょう。

◇    ◇    ◇    ◇

 それらは、またの機会に書くとして(まだ書くのか)、ソース量の違いもさることながら、Oracleは型や名前の整合性を取らないといけない場所があまりにも多いことが分かるでしょうか。

 弊社ではエクセルでジェネレートしています。つまり、仕様書を書くだけで上のソースは自動生成できますから、この時点での記述量の違いは苦にはならないように工夫をしていますけれど、やはり、開発後半でイロイロと変更が入るとSQL ServerとOracleでは大幅に工数が変わります。

 そのため、わたしはSQL Serverを推しています。でも、まぁ、経験は圧倒的にOracleの方が多いですけれど……。

 余談ですが、クライアント(DBサーバから見ればAPサーバもクライアントね)側の記述は

 ■ Oracle

SELECT * FROM TABLE(TEST_PKG.TEST_FUNC(?, ?))

 ■ SQLServer

EXEC TEST_FUNC ?, ?

 ここでも、SQLServerの方がスマートです。

 わたしがこれまで書いてきたような、ストアドプログラムで分離開発するなら、SQLServerの方が工数削減効果は圧倒的に高いです。わたしは経験が長いからどちらでも一緒ですけどね……。

 ちなみに、PostgreSQLは、Oracleに近いけれど、もう少し簡単な記述になります。

Comment(9)

コメント

choir

SQLServerは複文が使えるのもありがたいんですね。

ストアド禁止なんていう現場での逃げ道として、
同様の内容をそのままクライアントから実行できるので助かっています。

choirさん、こんにちは。

いわゆる無名ブロックってやつで、Oracleでも使えますよ。
いずれにしても、この辺の記述方法ではSQLServerに軍配が上がるかなと思います。

エントリーポート

生島さん、こんにちは。

パッケージができるようになったら、
sqlServer のほうが気にいると思います。

エントリーポートさん、こんにちは。

パッケージはやはり欲しい機能ですね。
次善の策として、私はプロシージャをパブリック、ファンクションをプライベートとすると良いと思います。
つまり、共通処理はすべてファンクションにする。

後は、SI Object Browser かな。コメントを一覧表示できるのと、検索が比較的やりやすいので、なんとかパッケージなしでも使えます。

choir

>生島さん
ありがとうございます。帰宅したら弄ってみます。
ここ4、5年ほどSQLServerばかり使っていたのでちょいと楽しみ:-)

生島さん、はじめまして。
以前の会社ではOracleが中心で、今の会社はSQL Serverが中心です。

SQL ServerがSELECT系のストアドが書きやすいというのは同感です。
たしかにSQL Serverのストアドは生産性が高いと思います。
また、生島さんも書いているように、前の会社ではストアドはほとんどバッチ処理でしか使っていませんでした。

しかし一方で今の会社でよく見かけるのはTemp Tableをまるでプログラムの変数のように使うストアドです。
極端なサンプルですが、イメージ的にはこんな感じです。

SELECT *
INTO #TEMP_DEPT
FROM DEPT
WHERE DEPT_NAME = 'KEIRI'

SELECT *
INTO #TEMP_EMP
FROM EMP A
INNER JOIN #TEMP_DEPT B
ON A.DEPT_ID = B.DEPT_ID
WHERE A.STATUS = 'ACTIVE'

--ここが最終結果
SELECT A.EMP_NAME, B.EMP_NAME AS SUPERVISOR_NAME
FROM #TEMP_EMP A
INNER JOIN EMP B
ON A.SUPERVISOR_ID = B.EMP_ID
WHERE B.STATUS = 'ACTIVE'

DROP TABLE #TEMP_DEPT, #TEMP_EMP

つまり、SQL一発で書けるような処理をわざわざTemp Tableに格納しながら結果を求めるのです。
SQLに慣れていないプログラマはSQLで宣言的に結果を求めるより、変数を使いながら手続き型で考える方が考えやすいんでしょうね。。。
しかし、手続き型で書いてしまうとデータベースによるクエリの最適化などがされにくいのでパフォーマンスは悪くなりがちです。

前の会社でオラクルを使っていた頃はストアドは使わないので必死に頭を使い、一発で求める結果が得られるようにSQLを組み立てていました。
今思うとあれはけっこう「SQL脳」を鍛えるのに役立っていたのではないかと思います。

ぴろしき

SQLServerは、UPDATE~FROMが使えるので、好きでした。
OracleのUPDATE~SET XXX=(SELECT~)・・・は、ちょっと。。DB2も同じですが。。。

Oracleでは、UNIONと同じ方式で、MINUSというのがあり、NOT EXISTSよりは、わかりやすいと思います。

DB2は、一番嫌いです。。。Z排他が出た日には、泣けてきました(T_T)

伊藤淳一さん、おはようございます。

例では最終出力のときのJOINでインデックスが使えないので最悪ですが、ワークテーブルを1つにするパターンならありかも知れません。ワークテーブルはメモリーに空きがあればそこを使うハズなので、それでロジックが分かりやすくなるなら良いと思います。

しかし、WEBシステムではAPサーバがコネクションプールしてコネクションを使い回すので、重ならないように工夫が必要ですね。

ぴろしきさん、おはようございます。

UPDATEの構文など「好き嫌い」の範囲ではないかと思います。
明確な差が出るのは、やはりストアドプロシージャかな。

コメントを投稿する