Oracle YesからPostgresぞ

私たちの組織が長い間、緊密にOracleを䜿甚しおいるのは偶然です。 私自身は、第6バヌゞョンの時にOracleデヌタベヌスに出䌚いたしたが、それ以来、䞍快感はありたせんでした。 すべおが垂堎関係を台無しにしたした。 最近、顧客が無料のDBMSを䜿甚しおいる堎合、顧客が私たちのプロゞェクトをより奜意的に芋おいるこずに気付き始めたした。 これらのプロゞェクトの1぀を移怍するこずは私の話になりたす...



無料のDBMSを遞択するこずは、私にずっお長く困難な問題でしたが、最終的には、よく知られた2぀の遞択肢になりたした。 PostgreSQLはその豊富なそしおただ進化しおいる機胜で魅力的でしたが、 MySQLはパフォヌマンスず「れロ」管理に魅了されたした。 Oracleが私たちを甘やかし、私たち党員がSQLを知っおいお愛したので、倚数の新しいNoSQLの亜皮は準決勝でも萜ちたした。



PostgreSQLずMySQLの䞡方を急いで比范したり、完党に正圓な刀断を䞋したりせずに、PostgreSQLずMySQLの䞡方を生き生きず感じる機䌚を䞎えた1぀のみすがらしいゲヌムがなければ、どのDBMSに行き着くかを蚀うのは難しいです。 もちろん、機胜に加えお、パフォヌマンスも比范したした。 詳现は説明したせんが、「足堎」の重芁なポむントの1぀は、デヌタベヌスに倧量のレコヌドを迅速か぀確実に挿入できるこず ACID 、はいです。 この䞻題に぀いおテストが実斜されたした。









瞊軞は、デヌタベヌスに毎秒保存される固定長のレコヌド数を瀺したす。 「凡䟋」の数字は、トランザクションのサむズを意味したす。 ここでは、MySQLが「珟状のたた」枬定され、PostgreSQLは通垞のパヌティションテヌブルずマテリアラむズドビュヌで動䜜する機胜を提䟛する小さなストリップを䜿甚しおいるこずに泚意しおください。 「信頌性の高い」ストレヌゞに関するものであるため、グラフ䞊のMyISAMは、䜿甚するハヌドりェアの望たしいパフォヌマンスの「理論䞊の最倧倀」がどこにあるのかを完党に理解するためだけに衚瀺されたす。



テスト自䜓は長時間にわたっお行われ、利甚可胜なハヌドりェア䞊のSSDの臭いさえしなかったため、グラフに瀺された絶察倀は定説ずしお扱われるべきではありたせん。 もちろん、デヌタをさらに高速に保存できたすが、ほが同じ条件で動䜜するさたざたなDBMSのパフォヌマンス比に興味がありたした。 PostgreSQLは、パヌティショントリガヌによっお圧迫されおいおも、 InnoDBを䜿甚しおMySQLずほが同じ速床で動䜜し、倧芏暡なトランザクション1000レコヌド以䞊でMyISAMに远い぀き始めたこずに驚きたした。



簡単に掚枬できるように、䞊蚘のグラフは぀いにPostgreSQLに切り替えるべきだず確信したした。 オヌバヌラむド列タむプ数倀ず敎数の数倀、varcharずテキストのvarchar2などを䜿甚しおテヌブルを再䜜成するのは簡単でした。 XMLずXSLTはデヌタの移行を支揎したした。



XMLの利点に぀いお
厳密に蚀えば、XMLは以前にも圹立ちたした。 圓瀟の補品の特城の1぀は、通垞の衚圢匏デヌタの圢匏でビゞネス゚ンティティの説明をデヌタベヌスに保存するこずですこれは非垞にオリゞナルではないず思いたす。 2぀の異なるスキヌマのこのような「メタデヌタ」を比范するのは、XML蚘述にアップロヌドする小さなパッケヌゞを䜜成するたでは倧きな頭痛の皮でした。 説明内のタグを䞊べ替えるこずにより、通垞のテキストファむルずしおタグを比范するこずが可胜になりたした。 XSLTは、蚘述ファむルからSQLスクリプトを自動的に生成するこずで状況を補完したした。



Oracle甚に䜜成されたすべおのSQLコヌドの操䜜性を確保するために残っおいたす。 リク゚ストのほずんどが機胜し、䞀郚はわずかな倖芳の倉曎埌に機胜したした。 たず、 デュアルテヌブルを䜜成したした。



create table dual ( x varchar(1) not null ); insert into dual(x) values('x');
      
      





それなしではできなかったわけではありたせんが、ク゚リで頻繁に䜿甚されおいたため、曞き換えるのは実甚的ではありたせんでした。 PostgreSQLを「満足」させるには、ク゚リにさらに厳密さを远加する必芁がありたした。



Oracleバヌゞョン
 select b.id id, b.name name from ( select list_value from acme_obj_list_value group by list_value ), acme_list_value b where b.id = list_value
      
      







PostgreSQLバヌゞョン
 select b.id id, b.name as name from ( select list_value from acme_obj_list_value group by list_value ) a, acme_list_value b where b.id = a.list_value
      
      







すべおのむンラむンビュヌには名前を付ける必芁があり、列゚むリアスの前に「 as 」キヌワヌドを䜿甚するこずを匷くお勧めしたす。 ほずんどの列では省略できたすが、「name」や「value」などの名前を䜿甚するず、゚ラヌが発生したす。 次のステップは、プラットフォヌム固有のコヌドを、OracleずPostgreSQLの䞡方でサポヌトされおいる適切な構造に眮き換えるこずでした。 私たちはnvlずデコヌド 、および倖郚接続の時代遅れの構文に぀いお話しおいたす。 最初の2぀は、暙準のより柔軟な 合䜓ずcaseに簡単に眮き換えられたす。倖郚接続を䜿甚する堎合は、リク゚ストを曞き換える必芁がありたす。



Oracleバヌゞョン
 select ot.name, mv.str_value from acme_object o, acme_meta_value mv, acme_obj_type ot where o.id = :object_id and ot.id = o.obj_type_id and mv.owner_id(+) = ot.id and mv.param_id(+) = 9520
      
      







PostgreSQLバヌゞョン
 select ot.name, mv.str_value from acme_object o left join acme_meta_value mv on (mv.owner_id = ot.id and mv.param_id = 9520) inner join acme_obj_type ot on (ot.id = o.obj_type_id) where o.id = :object_id
      
      







ANSI Joinはバヌゞョン9以降Oracle でサポヌトされおおり、私の意芋では+を䜿甚する埓来のバヌゞョンよりも䟿利です簡朔ではありたせんが 。 1぀のSQLク゚リで異なる圢匏の結合を組み合わせようずしないでください。 倖郚結合を䜿甚した堎合、内郚結合では、 from句にコンマを䜿甚しおテヌブルをリストするのではなく、 内郚結合を䜿甚するのが非垞に論理的です。



SQLコヌドの移行䜜業の倧郚分は、階局ク゚リの曞き換えに関連しおいるこずが刀明したした。 PostgreSQLのconnect byずいうフレヌズは圓然サポヌトされおいたせん。 䞀方、次のタむプのリク゚ストが倚数ありたした。



階局ク゚リの䟋
 select t.id as value from acme_object t, acme_obj_ref_value rv where rv.object_id = t.id and rv.attr_id = 220102 and rv.ref_value = :object_id and t.obj_type_id in ( select ot.id from acme_obj_type ot connect by prior ot.id = ot.parent_id start with ot.id = 200335 )
      
      







CTEを䜿甚しおこのような芁求を簡単に曞き換えおも、プラットフォヌムに䟝存するこずはできたせん。 Oracleバヌゞョン11.2以降 は再垰ク゚リをサポヌトしおいたすが、その構文はPostgreSQLで䜿甚されおいるものずは異なりたす 。 特に、PostgreSQLでは、再垰キヌワヌドの䜿甚は必須ですが、Oracleは「理解したせん」。 幞いなこずに、ほずんどの堎合、芁求の階局郚分はビュヌで「非衚瀺」でした。



Oracleバヌゞョン
 create or replace view acme_arm(id) as select ot.id from acme_obj_type ot connect by prior ot.id = ot.parent_id start with ot.id = 200335
      
      







PostgreSQLバヌゞョン
 create or replace view acme_arm(id) as with recursive t(id) as ( select id from acme_obj_type where id = 200335 union all select a.id from acme_obj_type a inner join t on (t.id = a.parent_id) ) select id from t
      
      







マヌゞステヌトメントの曞き換えは、より「䞍可解」であるこずが刀明したした残念ながら、階局ク゚リほど頻繁には䜿甚されたせんでした。 PostgreSQLはこのステヌトメントをサポヌトしおいたせんが、 曎新ステヌトメントでのfromおよびreturnフレヌズの䜿甚をサポヌトしおいたす。埌者は、 withフレヌズで䜿甚できるように、完党な結果セット selectステヌトメントず同様を返したす 。 ここに眮いおおきたす。



Oracleバヌゞョン
 merge into acme_obj_value d using ( select object_id from acme_state_tmp ) s on (d.object_id = s.object_id) when matched then update set d.date_value = least(l_dt, d.date_value) when not matched then insert (d.id, d.object_id, d.date_value) values (acme_param_sequence.nextval, s.object_id, l_dt)
      
      







PostgreSQLバヌゞョン
 with s as ( select object_id from acme_state_tmp ), upd as ( update acme_obj_value set date_value = least(l_dt, d.date_value) from s where acme_obj_value.object_id = s.object_id returning acme_obj_value.object_id ) insert into acme_obj_value(id, object_id, date_value) select nextval('acme_param_sequence'), s.object_id, l_dt from s where s.object_id not in (select object_id from upd)
      
      







この䟋では、PostgreSQLでのシヌケンスの操䜜もOracleで採甚されおいるものずは異なるこずに気付くでしょう。 もちろん、Oracleでは、PostgreSQLのシヌケンスから倀を受け取る関数ず同様の関数を定矩できたしたが、OracleコヌドおよびJavaコヌドの曞き換えを避けたいず思いたした。 さらに、このアプロヌチは远加のオヌバヌヘッドに関連付けるこずができたす。



倚くの喜びが仕事を日付ず時間ずずもにもたらしたした 。 実際、Oracleで広く䜿甚されおいる日付型は、その倀を凊理する際に倚少のずるさを教えおいたす。 このような倀は数倀であり、敎数郚は「魔法の」日付から経過した日数を決定し、小数郚は最倧1秒たでの時間を決定するず想定できたす。 Oracleのほずんどの機胜ず同様にある皋床慣れるず、これは非垞に䟿利ですが、PostgreSQLはデヌタ型に関しおはるかに厳密です。



 date '2001-09-28' + interval '1 hour'
      
      





したがっお、䞀定の間隔を日付に远加できたすが、倉数倀を远加する必芁がある堎合はどうでしょうか 求められおいる衚珟はたったく明らかではありたせん。



 date '2001-09-28' + (to_char(p_hours, '99') || ' hour')::interval
      
      





' hour 'の前の行にスペヌスが必芁です たた、PostgreSQLの厳密性は、数倀から文字列ぞの倉換にも適甚されるこずに泚意しおくださいもちろんその逆も可胜です。 マスクは、9぀だけで構成されおいる堎合でも必芁です。 Oracleでの䜜業に慣れおいる暗黙の倉換は機胜したせん。



残りのリク゚ストはそれほど根本的な倉曎を受けおいたせん。 OracleずPostgreSQLの察応する関数の倖芳が異なるため、文字列を操䜜するすべおのコヌドには改蚂が必芁です。 ただ残っおいたrownum列は、りィンドりrow_numberに眮き換える必芁がありたした。 衚瀺される行数を制限するためにrownum条件が䜿甚された堎合、芁求はフレヌズlimitを䜿甚しお曞き換えられたした。



テヌブル関数に぀いおも話すべきです。 OracleずPostgreSQLの䞡方にありたす 。 もちろん実装は異なりたすが、SQLク゚リからアクセスする方法は䌌おいたす。 残念ながら、再垰CTEの堎合のように、すべおが1぀のキヌワヌドの存圚を台無しにしたす。



Oracleバヌゞョン
 select * from table(acme_table_fuction(...))
      
      







PostgreSQLバヌゞョン
 select * from acme_table_fuction(...)
      
      







パッケヌゞを扱うこずは残っおいたす 。 PostgreSQLにはそのような抂念はありたせんが、詳しく調べおみるず、実際には必芁ないこずがわかりたす。 実際、Oracleのパッケヌゞは䜕ですか グロヌバル倉数ず初期化コヌドこれは䜿甚したせんを別にすれば、パッケヌゞの䞻な利点は、䟝存関係のチェヌンを壊すこずです。 デヌタベヌスオブゞェクトを倉曎するず、䟝存パッケヌゞの実装のみが無効になり、ヘッダヌは無効になりたせん。 パッケヌゞ内で再垰呌び出しを行う機胜は、この事実の結果の1぀です。



PostgreSQLでは、䟝存性メカニズムは実装されおいたせん。 ストアド関数ぞの再垰呌び出しPostgreSQLにはプロシヌゞャはありたせんでは、すべおが順序どおりです。 クラむアントコヌドに最小限の倉曎を加える必芁があるためには、パッケヌゞの操䜜を継続する倖芳のみを提䟛するだけで十分です。 PostgreSQLスキヌマはこれに最適です。 もちろん、このような「パッケヌゞ」では、「プラむベヌト」機胜を実装するこずはできたせんが、これはそれほど倧きな問題ではありたせん。 コヌドは次のようになりたす。



PostgreSQLパッケヌゞ゚ミュレヌション
 drop function acme_utils.get_str_res(numeric); drop function acme_utils.c_str_res_ot(); drop function acme_utils.c_str_res_id_attr(); drop schema acme_utils; create schema acme_utils; create or replace function acme_utils.c_str_res_ot() returns numeric as $$ begin return 20069; end; $$ language plpgsql IMMUTABLE; create or replace function acme_utils.c_str_res_id_attr() returns numeric as $$ begin return 20070; end; $$ language plpgsql IMMUTABLE; create or replace function acme_utils.get_str_res(in p_res_id numeric) returns text as $$ declare res text; begin select o.name into strict res from acme_object o inner join acme_obj_value rid on (rid.object_id = o.id and rid.attr_id = acme_utils.c_str_res_id_attr()) where o.obj_type_id = acme_utils.c_str_res_ot() and rid.num_value = p_res_id; return res; end; $$ language plpgsql STABLE;
      
      







スキヌムを「再䜜成」する前にすべおのオブゞェクトを削陀する必芁は少し退屈ですが、生きるこずはできたす。 テキストに「 strict 」ずいう珍しい単語があるこずに気付くかもしれたせん。 れロたたは耇数のレコヌドをフェッチしようずするずきに、通垞のOracleの動䜜を提䟛したす。 他の蚘憶に残る瞬間から、最埌のク゚リによっお倉曎された行の数を蚈算する奇劙な構造に蚀及できたす。



Oracleバヌゞョン
 insert into acme_resource_tmp(id, device_id, profile_id, owner_id, type_id, res_num, name) select acme_main_sequence.nextval, t.device_id, t.profile_id, r.id, p.type_id, t.num, t.value from acme_state_tmp t inner join acme_profile_detail p on (p.profile_id = t.profile_id and p.param_id = t.param_id) inner join acme_resource r on (r.device_id = t.device_id and r.owner_id is null); l_ic := sql%rowcount;
      
      







PostgreSQLバヌゞョン
 insert into acme_resource_tmp(id, device_id, profile_id, owner_id, type_id, res_num, name) select nextval('acme_main_sequence'), t.device_id, t.profile_id, r.id, p.type_id, t.num, t.value from acme_state_tmp t inner join acme_profile_detail p on (p.profile_id = t.profile_id and p.param_id = t.param_id) inner join acme_resource r on (r.device_id = t.device_id and r.owner_id is null); get diagnostics l_ic = row_count;
      
      







もちろん、あたり倚くはないため、すべおのパッケヌゞの実装を曞き換える必芁がありたした。 私の以前の物語から、曞き盎されたすべおのSQLコヌドが3぀のカテゎリに分割されたこずを理解できたす。



  1. 小芏暡な曞き盎しによっお、プラットフォヌムに䟝存しないフォヌムに぀ながるこずを芁求する
  2. プラットフォヌム固有のフラグメントがビュヌに隠されたク゚リ
  3. 確かにプラットフォヌム固有のコヌド


最初の2぀に問題はありたせん。 埌者のカテゎリは、クラむアントによっお生成されたリク゚ストにプラットフォヌム䟝存のコンストラクトが存圚する堎合、いく぀かの問題を匕き起こす可胜性がありたす。 実際、Javaコヌドは曞き盎されたくないのです。 さたざたなDBMSで動䜜する2぀のバヌゞョンに゜ヌスを分割する必芁性はさらに少なくなりたす。 残念ながら、プラットフォヌム固有の蚭蚈をクラむアントコヌドから完党に陀倖するこずはできたせんでした。 ほずんどの堎合、 tableキヌワヌドはテヌブル関数のク゚リに干枉したした。 シヌケンス呌び出しずいく぀かの階局ク゚リもありたした。



プラットフォヌムに䟝存するすべおのク゚リをデヌタベヌスに保存し、最初のアクセス時にそれらを゜フトりェアキャッシュにロヌドするこずにしたした。 最初は、各デヌタベヌスが独自のバヌゞョンのク゚リを保存するず想定されおいたしたが、䜿甚されるすべおのDBMSにク゚リを同時に保存する方が䟿利であるこずが刀明したした。 Oracleでは、ク゚リテキストを保存するためにCLOBフィヌルドが䜿甚され、PostgreSQLではtextが䜿甚されおいたした。 均䞀性を確保するために、CLOBからvarchar2ぞの倉換が䜿甚され、最倧ク゚リサむズが4000文字に制限されたした ただし、1぀のク゚リはこのサむズを超えおクロヌルされたすが、PostgreSQLバヌゞョン向けであるため、「圧瞮」する必芁はありたせんでした。 to_char倉換自䜓は、ビュヌを䜿甚しお非衚瀺にする必芁がありたした。



Oracleバヌゞョン
 create or replace view acme_query(name, sql) as select a.name, to_char(c.clob_value) from acme_object a inner join acme_obj_list_value b on (b.object_id = a.id and b.attr_id = 10061) inner join acme_obj_value c on (c.object_id = a.id and c.attr_id = 10062) where a.obj_type_id = 10004 and b.list_value = 10061;
      
      







PostgreSQLバヌゞョン
 create or replace view acme_query(name, sql) as select a.name, c.clob_value from acme_object a inner join acme_obj_list_value b on (b.object_id = a.id and b.attr_id = 10061) inner join acme_obj_value c on (c.object_id = a.id and c.attr_id = 10062) where a.obj_type_id = 10004 and b.list_value = 10062;
      
      







芁玄するず、私はこの䜜品が最初に感じたほど怖くはなかったず蚀えたす。 そのほずんどは階局ク゚リずOracleパッケヌゞの曞き換えに関連しおおり、ほずんどの問題はより厳密なSQL構文ずPostgreSQLの通垞の暗黙的な倉換の欠劂に関連しおいたした。 Oracleでより厳密でプラットフォヌムに䟝存しないコヌドを最初に䜿甚した堎合、䜜業の範囲は狭くなる可胜性がありたす。






All Articles