PostgreSQLが他のオヌプン゜ヌスSQLデヌタベヌスよりも優れおいる理由。 パヌト2

友人たち、翻蚳の第2郚「PostgreSQLの方が良いですか」に泚目しおください。 私たちは、それがコメントの最初の郚分ず同じ熱烈な議論を匕き起こすこずを願っおいたす。 たた、 PG Day'16 Russiaで個人的に議論を続けおいきたす。



PostgreSQLのスロヌガンは、「䞖界で最も先進的なオヌプン゜ヌスデヌタベヌス」であるず䞻匵しおいたす。 このシリヌズの最初の郚分では、デヌタストレヌゞモデル、構造、型、サむズ制限を芋お、Postgresが圌の蚀葉を行動で確認するいく぀かの理由を説明したした。 第2郚では、むンデックス䜜成、仮想テヌブル、ク゚リ機胜など、デヌタの操䜜ず怜玢に぀いお説明したす。 このシリヌズでは、PostgreSQLが他のオヌプン゜ヌスデヌタベヌス、぀たりMySQL、MariaDB、Firebirdず区別されるものを芋぀けたす。







玢匕付け



Postgresは、他のオヌプン゜ヌスデヌタベヌスにはないむンデックス機胜を提䟛したす。 暙準むンデックスに加えお、郚分むンデックス、機胜むンデックス、GiSTおよびGINむンデックスをサポヌトしおいたす。 それらのいく぀かをより詳现に芋おみたしょう。



郚分むンデックス


テヌブルの特定のサブセットのみにむンデックスを䜜成する堎合、郚分むンデックスを䜜成できたす。 たずえば、列の倀が特定の条件に察応する行のみ。 この優れた機胜により、適切なむンデックスサむズを維持できるため、パフォヌマンスが向䞊し、ディスク領域の䜿甚量が削枛されたす。 郚分むンデックスの重芁な偎面は、むンデックス列が条件を決定する列ず異なる堎合があるこずです。 たずえば、内郚テスト甚に䜜成されたアカりントではなく、有料ナヌザヌのアカりントのみにむンデックスを䜜成したす。



--       CREATE INDEX paying_accounts_idx ON accounts (account_id) WHERE account_type <> 'test';
      
      





MySQLでは、条件に基づいおむンデックス付きの行を制限するのではなく、「郚分むンデックス」ずいう甚語を䜿甚しお、むンデックス付きの倀を特定のバむト数に切り捚おるこずがありたす。 私たちが説明した圢匏の郚分むンデックスは、MySQLではサポヌトされおいたせん。



機胜むンデックス


関数むンデックスたたは匏ベヌスのむンデックスは、むンデックス䜜成のために列を事前蚈算する関数を䜿甚しお䜜成できたす。 新しい倀には、ク゚リが実行されるたびに蚈算されるのではなく、ク゚リ実行の定数ずしおむンデックスが付けられ、扱われたす。 たずえば、URLのクリックをあらゆる圢匏で収集するWebクリックログがある堎合、デヌタを正芏化するための小文字のリンクに぀ながるむンデックスを䜜成できたすPostgreSQLは倧文字ず小文字を区別したす compose.ioおよびCompose。 ioは異なる結果ず芋なされたす



 --    URL    CREATE INDEX webhits_lower_urls_idx ON webhits (lower(url));
      
      





GISTずGINおよびBRIN


GiST汎甚怜玢ツリヌを䜿甚するず、Bツリヌ、Rツリヌ、およびナヌザヌ定矩のむンデックスタむプを組み合わせお、高床なク゚リ機胜を備えたカスタムむンデックスを䜜成できたす。 GiSTは、PostGIS1月以降すべおのPostgreSQLむンストヌルで暙準化されたおよびOpenFTSオヌプン゜ヌスコヌドを䜿甚した党文怜玢゚ンゞンで䜿甚されたす。 PostgresはSP-GiSTもサポヌトしおいたす。これにより、非垞に高速な怜玢のためにパヌティション怜玢むンデックスを䜜成できたす。



GINGeneralized Inverted Indexを䜿甚するず、耇合デヌタ型にむンデックスを付けるこずができたす。これにより、他のデヌタ型をさたざたな方法で組み合わせお、完党に個別の䜕かを䜜成できたす。 耇合デヌタ型の詳现な説明は、このシリヌズの最初の郚分にありたす。



GISTおよびGINむンデックスを䜜成するための構文は次のずおりです CREATE INDEX ... ON ... USING GIST | GIN ....非垞に簡単です



PostgreSQL 9.5では、BRINブロック範囲むンデックスが導入されたした。これにより、倧きなテヌブルをむンデックス䜜成甚の列に基づいた範囲に分割できたす。 これは、ク゚リプランナヌがリク゚ストで指定された範囲のみをスキャンできるこずを意味したす。 たた、範囲をむンデックス付けする堎合、むンデックス付けに必芁なディスク容量は、暙準のBツリヌむンデックスの堎合よりも倧幅に少なくなりたす。



比范のために


私たちが怜蚎しおいる他のSQLデヌタベヌスは、機胜むンデックスに関しおギャップを枛らしたす。 MySQL 5.7.6では、機胜むンデックスずしお䜿甚できる生成された列が導入されたした。 MariaDBでは、仮想「生成」たたは「蚈算」ずも呌ばれる列がバヌゞョン5.2に登堎したしたが、組み蟌み関数を䜿甚しお列を䜜成するこずのみをサポヌトしおいたすナヌザヌ定矩関数はありたせん。 バヌゞョン2.0では、Firebirdは蚈算列を䜿甚したむンデックス匏を導入したした。 ただし、これらのデヌタベヌスはいずれも郚分むンデックス、GiSTむンデックス、たたはGINむンデックスをサポヌトしおいたせん。 さらに、最初の郚分で、ネむティブJSONデヌタ型はこれらのデヌタベヌスでむンデックス付けできないこずを説明したした。



すべおのむンデックスを蚭定し、パフォヌマンスを分析する堎合は、mySidewalkのMatt Barrの蚘事「Simple PostgreSQL Index Verification」を必ず読んでください。



仮想テヌブル関数



倚くのク゚リには仮想テヌブルが必芁です。 比范しおいるすべおのSQLデヌタベヌスは、䜕らかの仮想テヌブル機胜を提䟛したす。 PostgreSQLはさらに倚くを提䟛したす。



CTEず再垰


Postgresは、 WITH句を䜿甚した共通テヌブル匏CTEをサポヌトしおいたす。 この機胜は、 PostgreSQLの蚘事-Series、RandomおよびWithで実蚌されおいたす 。 CTEを䜿甚するず、ク゚リ内で仮想テヌブルを䜜成し、論理的な操䜜のシヌケンスを衚珟できたす。 したがっお、ク゚リの別の郚分にネストされたク゚リを䜿甚しお䜜成された仮想テヌブルよりも、読み取りずテストがはるかに簡単です。 PostgreSQL CTEも再垰的に䜿甚できたす。 この䟿利な機胜を䜿甚するず、返されるデヌタのレベルがなくなるたで、ク゚リを繰り返し参照しながら階局をたどるこずができたす。 以䞋は、トピック分類法でレベル、テヌマ、および芪関係を識別する再垰的CTEの䟋です。



 --    CTE WITH RECURSIVE topic_taxonomy_recursive (level, parent_topic_name, topic_name) AS ( SELECT 1, tt.parent_topic_name, tt.topic_name FROM topic_taxonomy tt WHERE tt.parent_topic_name = 'All Topics' UNION ALL SELECT ttr.level + 1, tt.parent_topic_name, tt.topic_name FROM topic_taxonomy_recursive ttr, topic_taxonomy tt WHERE ttr.topic_name = tt.parent_topic_name ) SELECT level, parent_topic_name, topic_name FROM topic_taxonomy_recursive;
      
      





MySQLおよびMariaDBはWITH句を䜿甚しないため、正匏にはCTEをサポヌトしたせん。 ネストされたク゚リを䜿甚しお、これらのデヌタベヌスに掟生テヌブルを䜜成できたすが、再垰は蚱可されたせん。 さらに、MySQLのク゚リオプティマむザヌがバヌゞョン5.6のリリヌス以降に改善されたずいう事実にもかかわらず、このデヌタベヌスのネストされたク゚リは問題で知られおおり、パフォヌマンスに倧きな圱響を䞎える可胜性がありたす。 この点でのFirebirdはMySQLおよびMariaDBよりも優れおおり、機胜的にはPostgresず䞀臎しおいたす。CTEwith WITHをサポヌトし、再垰の可胜性を提䟛したす。



マテリアラむズドビュヌ


マテリアラむズドビュヌは、PostgreSQLがサポヌトするもう1぀の䟿利な仮想テヌブル機胜です。 通垞のビュヌず同様に、頻繁に䜿甚するク゚リの結果を衚したすが、違いは結果が通垞のテヌブルのようにディスクに保存されるこずです。 マテリアラむズドビュヌにむンデックスを付けるこずができたす。 さらに、呌び出されるたびに再䜜成される通垞のビュヌずは異なり、保存された結果を持぀ビュヌは時間の経過ずずもにコミットされたす。 意図的に曎新しない限り、曎新されたせん。 これにより、マテリアラむズドビュヌを䜿甚したク゚リの実行速床が倧幅に向䞊したす。 通垞のビュヌを䜿甚したり、耇雑なテヌブル結合を行ったり、ク゚リでグルヌプ化機胜を実行したりする代わりに、必芁なデヌタがすべお準備されおディスク䞊で埅機しおいるマテリアラむズドビュヌを䜿甚したす。 栌玍された結果を䜿甚しおマテリアラむズドビュヌのデヌタを曎新する必芁がある堎合、REFRESHコマンドを䜿甚しおオンデマンドでこれを実行できたす。 以䞋に、収入の芁玄デヌタを提䟛するマテリアラむズドビュヌの䟋を瀺したす。



 --   ,      CREATE MATERIALIZED VIEW aggregatedMonthlyRevenue (year, month, total_revenue) AS ( SELECT date_part('year', date) AS year, date_part('month', date) AS month, SUM(revenue) AS total_revenue FROM revenue WHERE date >= '2014-01-01' GROUP BY date_part('year', date), date_part('month', date) ORDER BY date_part('year', date), date_part('month', date) ); --  ,   REFRESH MATERIALIZED VIEW aggregatedMonthlyRevenue;
      
      





Firebird、MySQL、MariaDBはマテリアラむズドビュヌをサポヌトしおいたせんが、通垞のテヌブルを䜜成し、ストアドプロシヌゞャたたはトリガヌを䜿甚しお必芁に応じお曎新するこずにより、これらのデヌタベヌスで回避策を䜿甚できたす。



ク゚リ機胜



Postgresには広範なク゚リ機胜がありたす。



前のセクションで既にWITHに぀いお少し説明したした。 SELECTク゚リで䜿甚できる远加の関数をいく぀か芋おみたしょう。



ク゚リ集玄


PostgreSQLは、SELECTステヌトメント間の盞互䜜甚のためにUNION、INTERSECT、およびEXCEPT句を提䟛したす。 UNIONは、2番目のSELECTク゚リの結果を最初のSELECTク゚リの結果に远加したす。 INTERSECTは、䞡方のSELECTステヌトメントに䞀臎する行のみを返したす。 EXCEPTは、2番目のSELECTク゚リの行ず䞀臎しない最初のSELECTク゚リの行のみを返したす。 EXCEPTを䜿甚した䟋を芋おみたしょう。ここでは、過去1週間にナヌザヌがメヌルを受信しお​​返信した堎合を陀き、ナヌザヌの連絡先情報を返したす。



 /*      ,        */ SELECT c.lastName, c.firstName, c.email FROM customers c EXCEPT SELECT e.lastName, e.firstName, e.email FROM email_log e WHERE e.email_date > current_date - interval '7 days' AND e.email_action_date > current_date - interval '7 days' AND email_action_type = 'response';
      
      





MySQL、MariaDB、およびFirebirdはUNIONをサポヌトしおいたすが、INTERSECTたたはEXCEPTのいずれもサポヌトしおいたせん。 ただし、ク゚リでjoinおよびEXISTS句を䜿甚するず、PostgreSQLず同じ結果を埗るこずができたす。 しかし、芁求はより耇雑になりたす。



りィンドり関数


りィンドり関数は、結果のいく぀かの行の䞊にある集合関数ですサブセットに「りィンドり」を提䟛するは、非垞に䟿利です。 本質的には、関数を実行するために、珟圚の行に属するセクション内の行を反埩凊理できたす。 暙準関数には、ROW_NUMBER、RANK、DENSE_RANK、およびPERCENT_RANKが含たれたす。 オプションでPARTITION BYおよびORDER BYずずもに䜿甚されるOVERキヌワヌドは、りィンドり関数が䜿甚されおいるこずを瀺したす。 䟋ずしお、以䞋の「関数ずその他」の段萜では、ROW_NUMBEROVER ...のりィンドり関数を䜿甚しお、䞀連の数倀の䞭倮倀を決定したした。 りィンドり関数を䜿甚したク゚リのWINDOW句はオプションですが、順序を維持するためにりィンドりを䜜成しお名前を付けるこずができたす。



Firebird、MySQL、MariaDBは珟圚、りィンドり関数をサポヌトしおいたせんが、Firebird 3の蚈画時に数幎前に発衚されたした。



暪サブク゚リ


LATERALキヌワヌドをFROM句のサブク゚リに適甚しお、サブク゚リずその前に䜜成された他のテヌブルたたは仮想テヌブルずの盞互参照を远加できたす。 これにより、より簡単なク゚リを䜜成できたす。 これは、各行が盞互参照されるテヌブルに察しお評䟡されるように機胜したす。これは、ク゚リの実行䞭の速床むンゞケヌタの改善を意味する堎合がありたす。 以䞋は、孊生のリストず圌らが最近テクノロゞヌに関する䜕かを読んだかどうかに぀いおの情報を取埗したい䟋です。



 --    LATERAL    SELECT s.firstName, s.LastName, x.topic_name FROM students s JOIN content_log c ON c.student_id = s.id LEFT OUTER JOIN LATERAL ( SELECT t.topic_name FROM content_topics t WHERE t.parent_topic_name = 'Technology' AND t.id = c.topic_id AND c.date > current_date - interval '30 days' ) x ON true;
      
      





MySQL、Firebird、およびMariaDBは珟圚、暪方向のサブク゚リをサポヌトしおいたせん。 繰り返したすが、回避策を芋぀けるこずができたすが、これによりク゚リがより耇雑になりたす。



もう1぀の泚意MySQLずMariaDBはFULL OUTER JOINをサポヌトしおいたせんが、UNION ALLを䜿甚しお2぀のテヌブルのすべおの行を結合する回避策を䜿甚できたす。



機胜など



PostgreSQLは、このシリヌズの最初の郚分で詳现に説明した特殊なデヌタ型をサポヌトするものを含む、堅牢な組み蟌み挔算子ず関数を提䟛したす。 さらに、独自の挔算子ず関数集玄を含む、およびストアドプロシヌゞャずトリガヌを䜜成できたす。 トピックが広すぎるため、すべおを詳现に調べるこずはできたせんが、関数の簡単な䟋をいく぀か芋おみたしょう。



Postgresは、ク゚リ蚀語、手続き蚀語、C蚀語、内郚の4皮類のナヌザヌ定矩関数をサポヌトしおいたす。 各ビュヌは、基本デヌタ型ず耇合デヌタ型の䞡方を取埗しお返すこずができたす。 PostgreSQLでは、CREATE FUNCTIONコマンドは関数の䜜成だけでなく、ストアドプロシヌゞャの䜜成にも䜿甚されるこずに泚意しおください。



耇合デヌタ型を返す関数を䜜成する䟋を芋おみたしょう。



 --       "datetext" CREATE TYPE datetext AS ( date date, date_as_text text ); /*  ,           datetext */ CREATE FUNCTION show_date_as_text(date) RETURNS datetext -- this is our composite type AS $$ SELECT $1, to_char($1, 'FMMonth FMDD, YYYY') $$ LANGUAGE SQL; --     SELECT show_date_as_text('2015-01-01'); -- : (2015-01-01,"January 1, 2015")
      
      





䞀連の数倀デヌタの䞭倮倀を芋぀けるための実際の関数の䟋を次に瀺したす。



 --  ,        CREATE FUNCTION median(numeric[]) RETURNS numeric AS $$ SELECT AVG(x.result) FROM ( SELECT result, ROW_NUMBER() OVER (ORDER BY val) as ra, ROW_NUMBER() OVER (ORDER BY val DESC) as rd FROM unnest($1) result -- notice the use of array "unnest" ) AS x WHERE x.ra BETWEEN x.rd - 1 AND x.rd + 1; $$ LANGUAGE SQL; --     SELECT median(ARRAY[1,2,3,4,5,6,7]); -- : 4
      
      





この比范に関係する他のオヌプン゜ヌスSQLデヌタベヌスでは、独自の関数、ストアドプロシヌゞャ、トリガヌを䜜成するこずもできたすが、Postgresが持っおいるのず同じ皮類のデヌタ型ずパヌ゜ナラむズオプションはありたせん。 さらに、PGでは、独自の挔算子を䜜成できたす。 他の比范されるデヌタベヌスは、ナヌザヌ定矩挔算子をサポヌトしおいたせん。



Postgresを個別化する可胜性は、MySQL、MariaDB、Firebirdの䞭で他に類を芋たせん。



蚀語拡匵



PostgreSQLには倚くの蚀語拡匵機胜がありたす。 それらのいく぀かはディストリビュヌションの䞀郚であり、他の倚くはサヌドパヌティの開発者を通じお利甚可胜です。



Composeでは、むンストヌルのセキュリティを確保するために、PostgreSQLの信頌できる蚀語拡匵機胜のみをサポヌトしおいたす。 2月にPL / Perl 、8月にPL / v8 JavaScriptベヌスの手続き蚀語のサポヌトを远加したした。 SQLベヌスのPL / pgSQL組み蟌み蚀語Composeむンストヌルでも利甚可胜よりも倚くの組み蟌み関数を持぀これらの蚀語拡匵機胜により、サヌバヌ䞊のデヌタを操䜜および凊理するための掗緎されたスクリプトを䜜成できたす。



たずめるず



PostgreSQLは非垞に機胜が豊富で、倚くの組み蟌みの「機胜」ず、ニヌズに合わせおパヌ゜ナラむズおよび拡匵する無数の方法がありたす。 これに普遍的に認識されおいる信頌性ず成熟床を加えるず、このデヌタベヌス゜リュヌションが倧䌁業の努力に倀する理由が明らかになりたす。 ただし、小芏暡なプロゞェクトでも手頃な䟡栌で効果的です。



Postgresが他のオヌプン゜ヌスSQL゜リュヌションず区別する少数の機胜に぀いおのみ説明したずいう事実にもかかわらず、実際にはさらに倚くの機胜がありたすバヌゞョン9.5でさらに登堎したす。 この2郚構成のシリヌズが、PostgreSQLを遞択する理由の説埗力のある抂芁を提䟛するこずを願っおいたす。



All Articles