SQL の互換性

リレーショナル・データベース製品だったらみんな同じ SQL が使えて互換性がある、なんて思っていませんか??

うっかりすると、DB2 for IBM i だけあまり互換性がない、などと思っていたりしませんか?!
(ちなみに、以前「DB2 for i と DB2 for LUW の違い」でも紹介しましたが、SQL のレベルで DB2 for LUW との差はほとんどありません)

今回は例を元に実際にはどうなっているかを簡単に見ていきたいと思います。

Refactoring SQL Applications」という本から、サンプルとなるテーブルを作るDDLの一番最初のものがこちらです。

 MySQL

-- --------------------------------------------------------

--

-- Table structure for table 'area_accounts'

--  

DROP TABLE area_accounts;

CREATE TABLE area_accounts (

  areaid int(11) NOT NULL,

  accountid bigint(20) NOT NULL,

  PRIMARY KEY  (areaid,accountid)

);  

Oracle

-- --------------------------------------------------------

--

-- Table structure for table 'area_accounts'

--  

DROP TABLE area_accounts;

CREATE TABLE area_accounts (

  areaid    number  NOT NULL,

  accountid number NOT NULL,

  PRIMARY KEY  (areaid,accountid)

); 

SQL Server

-- --------------------------------------------------------

--

-- Table structure for table 'area_accounts'

--  

USE [refactoring]

GO

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE TABLE [dbo].[area_accounts](

        [areaid] [numeric](11, 0) NOT NULL,

        [accountid] [numeric](20, 0) NOT NULL,

PRIMARY KEY CLUSTERED

(

        [areaid] ASC,

        [accountid] ASC

)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]

) ON [PRIMARY]

 GO

 

どうでしょう??
 一見して(SQL Server などは特に)かなり違っているのがわかりますね。

また、それぞれのカラムのデータタイプを見てみると、

MySQL では int(xx)、Oracle では number、SQL Server では numeric(xx,x)

とまったくこんなところからして異なっているのがわかります。

ちなみに、いわゆる"標準SQL" と IBM i 版も含めたDB2 用に書くと、下記のようになります。

【標準SQL・DB2

DROP TABLE area_accounts;

CREATE TABLE area_accounts (

  areaid integer NOT NULL,

  accountid integer NOT NULL,

  PRIMARY KEY  (areaid,accountid)

);

 

DMLについても見てみましょう。
これも同じ本の最初の方から例を取ってみます。

MySQL

select txid,amount,curr from transactions where accountid=? and txdate >= date_sub(?, interval 30 day) order by txdate

Oracle

select txid,amount,curr from transactions where accountid=? and txdate >= to_date(?, 'DD-MON-YYYY') - 30 order by txdate

SQL Server

select txid,amount,curr from transactions where accountid=? and txdate >= DATEADD (DD, 30 ,? ) order by txdate

  

 やはりそれぞれ全然同じではないことがわかりますね。

DB2用に書き直すと下記のようになります。
上記 3者とは異なりベンダー固有の関数などは使用しないで、直接日付の計算を行うようになっています。

 

DB2

select txid,amount,curr from transactions where accountid=? and txdate >= (cast(? as timestamp) - 30 days) order by txdate

手元にあった本のあたまの方からちょっと例を拾ってみただけでも、これくらい各データベース製品によってSQLは異なっているわけです。

Oracle には長い歴史がありますし、SQL Server 自体はかなり新しい製品ですがその前身である Sybase にも長い歴史がありますので、こういった"方言"はある意味しかたのないことでしょう。

ではなぜDB2 は標準SQL とほとんど同じなのか? というと、
そもそもSQL というもの自体がもともとIBM の製品として出てきた経緯もあり、
結果的に標準のSQL とほとんど同じものになっている、というわけなんです。

ただ、どの"方言"にせよ実現できることは基本的に変わりません。
本質を理解していれば書き換えるのにそれほどの手間はかかりません。

もし、すでにどれかの製品のSQL のエキスパート、というわけではないのであれば、まずはいわゆる「標準SQL」で基本をおぼえるのがいいのではないでしょうか。
DB2 for IBM i のSQL も、固有の拡張がほとんどない基本に忠実で素直なSQL なので、学習のとっかかりとしてはとてもいいと思いますよ。

iForum: SQL の互換性
http://www.iforum.ne.jp/article.php/20090227031628310