web-dev-qa-db-ja.com

なぜリレーショナルデータベースはネストされた形式で情報を返すことをサポートしないのですか?

投稿とコメントを付けたいブログを作成しているとします。そこで、2つのテーブルを作成します。自動インクリメントの整数「id」列を持つ「posts」テーブルと、外部キー「post_id」を持つ「comments」テーブルです。

次に、おそらく最も一般的なクエリ、つまり投稿とそのすべてのコメントを取得するクエリを実行します。リレーショナルデータベースはかなり新しいので、私にとって最も明白に見えるアプローチは、次のようなクエリを作成することです。

SELECT id, content, (SELECT * FROM comments WHERE post_id = 7) AS comments
FROM posts
WHERE id = 7

これにより、必要な投稿のIDとコンテンツ、および配列(JSONで使用するようなネストされた表現)に適切にパッケージ化されたすべての関連するコメント行が得られます。もちろん、SQLとリレーショナルデータベースはこのように機能しません。最も近いのは、「投稿」と「コメント」を結合して、データの不必要な重複を多数返すことです(同じ投稿情報が繰り返されます)。すべての行で)、つまり、すべてをまとめるためのデータベースと、すべてを解析して元に戻すためのORMの両方で処理時間が費やされます。

ORMに投稿のコメントを積極的に読み込むように指示したとしても、投稿に対する1つのクエリをディスパッチし、次に2番目のクエリですべてのコメントを取得して、クライアント側でそれらをまとめることが最善です。また、非効率的です。

リレーショナルデータベースは実証済みの技術(地獄、私よりも古い)であり、何十年にもわたって膨大な量の研究が行われていることを理解しています。また、それらには本当に良い理由があると確信しています(そしてSQL標準)は、それらが機能するように機能するように設計されていますが、上で概説したアプローチがなぜ不可能なのかはわかりません。レコード間の最も基本的な関係の1つを実装する最も単純で明白な方法であるように私には思えます。なぜリレーショナルデータベースはこのようなものを提供しないのですか?

(免責事項:私は主にRailsとNoSQLデータストアを使用してwebappsを記述していますが、最近Postgresを試してみましたが、実際にとても気に入っています。リレーショナルデータベースを攻撃するつもりはありません。私は困惑しているだけです。)

Railsアプリを最適化する方法や、特定のデータベースでこの問題を回避する方法を尋ねているのではありません。直観に反しているように思われる場合に、SQL標準がこのように機能する理由を尋ねています。 SQLの元の設計者が結果をこのようにしたかったのには、いくつかの歴史的な理由があるに違いありません。

46

C. J.デートはこれについて、第7章と SQLとリレーショナル理論 の付録Bで詳しく説明しています。そうです、すべての行でsame関係型である限り、属性のデータ型が関係自体であることを禁止する関係理論には何もありません。あなたの例は適格です。

しかし、関係の階層が非対称であるため、このような構造は「通常-常にではないが-禁忌」(つまり、悪い考え)であると日付は述べています。たとえば、入れ子構造からおなじみの「フラット」構造への変換は、入れ子を再作成するために常に逆にすることができるとは限りません。

クエリ、制約、および更新はより複雑であり、作成が難しく、RDBMSが関係値属性(RVA)を許可する場合にサポートすることが難しくなります。

また、リレーションシップのbest階層があまり明確ではないため、データベース設計の原則が曖昧になります。特定のサプライヤーによって提供された部品のネストされたRVAを使用してサプライヤーの関係を設計する必要がありますか?または、特定の部品を供給するサプライヤーのために、部品とネストされたRVAとの関係ですか?または、両方を保存して、さまざまな種類のクエリを簡単に実行できるようにしますか?

これは 階層型データベース および ドキュメント指向データベース モデルから生じるジレンマと同じです。最終的に、ネストされたデータ構造へのアクセスの複雑さとコストにより、設計者はデータを重複して格納し、さまざまなクエリで簡単に検索できるようになります。リレーショナルモデルは冗長性を妨げるため、RVAはリレーショナルモデリングの目標に反する可能性があります。

私が理解していること(私はそれらを使用していません)から、 Rel および Dataphor は、関係値属性をサポートするRDBMSプロジェクトです。


@dportasからの再コメント:

構造化タイプはSQL-99の一部であり、Oracleはこれらをサポートしています。ただし、ベーステーブルの行ごとにネストされたテーブルに複数のタプルは格納されません。一般的な例は、「住所」属性です。これは、ベーステーブルの単一の列のように見えますが、ストリート、都市、郵便番号などのサブ列があります。

Nested tables もOracleでサポートされており、これらはベーステーブルの行ごとに複数のタプルを許可します。しかし、これが標準SQLの一部であることは知りません。また、1つのブログの 結論 を覚えておいてください。「ネストされたテーブルをCREATE TABLEステートメントで使用することは決してありません。すべての時間を費やして、ネストを解除して再び役立つようにします!」

42
Bill Karwin

初期のデータベースシステムの一部は 階層データベースモデル に基づいていました。これは、ここで提案しているように、親と子を持つツリーのような構造でデータを表しています。 HDMSの大部分は、リレーショナルモデルに基づいて構築されたデータベースに取って代わられました。これの主な理由は、RDBMSが階層データベースでは困難だった「多対多」の関係をモデル化できることと、RDBMSが元の設計の一部ではないクエリを簡単に実行できるのに対し、HDBMSは設計時に指定されたパスを介してクエリするように制限したことです。

階層型データベースシステムの例、特にWindowsレジストリとLDAPはまだいくつかあります。

この主題の広範な報道は以下で利用可能です 記事

15
Steve Weet

あなたの質問は、データベースは確かな論理に基づいて理論上の根拠を設定し、参照の整合性、同時実行性を確保しながら、データを(2次元)セットに格納、操作、および取得するという非常に優れた仕事をしているという事実に本当に集中していると思いますその他多くのことは、オブジェクト指向形式または階層形式と呼ばれるものでデータを送信(および受信)する(追加の)機能を提供していません。

次に、「投稿のコメントを熱心に読み込むようにORMに指示した場合でも、最善の方法は、投稿に対して1つのクエリをディスパッチし、次に2番目のクエリですべてを取得することです。コメントの次に、それらをクライアント側にまとめます。これも非効率的 "です。

2つのクエリを送信し、結果の2つのバッチを受信するのに非効率的な部分はありません。

--- Query-1-posts
SELECT id, content 
FROM posts
WHERE id = 7


--- Query-2-comments
SELECT * 
FROM comments 
WHERE post_id = 7

私はそれが(ほとんど)最も効率的な方法であると主張します(あなたが本当にposts.idを必要とせず、comments.*のすべての列を必要としないので)

トッドが彼のコメントで指摘したように、表示する準備ができているデータを返すようにデータベースに要求するべきではありません。それを行うのはアプリケーションの仕事です。 (1つまたはいくつかの)クエリを記述して、すべての表示操作に必要な結果を取得できます。これにより、db(またはメモリバス)を介してdbからアプリケーションに送信されるデータに不要な重複がなくなります。

私はORMについて実際に話すことはできませんが、おそらくその一部は私たちのためにこの仕事の一部を行うことができます。

同様の手法は、Webサーバーとクライアント間のデータの配信にも使用できます。他の手法(キャッシングなど)を使用して、データベース(またはWebや他のサーバー)が重複した要求で過負荷にならないようにします。

私の推測では、SQLのような標準は、1つの領域に特化したままで、フィールドのすべての領域をカバーしようとしない場合に最適です。

一方、SQL標準を設定する委員会は、将来的に別の方法で考え、そのような追加機能の標準化を提供する可能性があります。しかし、それは一晩で設計できるものではありません。

10
ypercubeᵀᴹ

私は適切な議論の答えで答えることができませんので、私が間違っている場合は遠慮なく私を忘却に投票してください(ただし、新しいことを学ぶことができるように修正してください)。その理由は、リレーショナルデータベースがリレーショナルモデルに集中しているということだと思います。リレーショナルモデルは、「一次論理」と呼ばれるものについて何も知らないことに基づいています。あなたが質問するかもしれないことは、おそらく、数学的/論理的フレームワークに概念的に適合しないリレーショナルデータベースが構築されていることです。さらに、あなたが尋ねることは一般的にグラフデータベースによって簡単に解決され、達成したいことと矛盾するのはデータベースの根本的な概念化であるというより多くのヒントを与えます。

5
Stefano Borini

FOR XMLを使用する場合、少なくともSQLServerがネストされたクエリをサポートすることは知っています。

SELECT id, content, (SELECT * FROM comments WHERE post_id = posts.id FOR XML PATH('comments'), TYPE) AS comments
FROM posts
WHERE id = 7
FOR XML PATH('posts')

ここでの問題は、RDBMSからのサポートの欠如ではなく、テーブル内のネストされたテーブルのサポートの欠如です。

さらに、内部結合を使用できない理由は何ですか?

SELECT id, content, comments.*
FROM posts inner join comments on comments.post_id = posts.id
WHERE id = 7

実際には、内部結合を入れ子になったテーブルとして見ることができます。最初の2つのフィールドの内容のみが繰り返されます。結合のパフォーマンスについてはあまり心配していません。このようなクエリの唯一の遅い部分は、データベースからクライアントへのioです。これは、コンテンツに大量のデータが含まれている場合にのみ問題になります。その場合、2つのクエリを提案します。1つはselect id, contentと、内部結合とselect posts.id, comments.*。 2つのクエリしか使用しないので、これは複数の投稿があってもスケールします。

5
Dorus

実際には、Oracleは必要なものをサポートしていますが、サブクエリを「カーソル」キーワードでラップする必要があります。結果はオープンカーソルを介してフェッチされます。 Javaでは、たとえばコメントは結果セットとして表示されます。詳しくは、Oracleのドキュメント "CURSOR Expression" を参照してください。

SELECT id, content, cursor(SELECT * FROM comments WHERE post_id = 7) AS comments
FROM posts
WHERE id = 7
5

一部はネストをサポートしています(階層)。

1つのクエリが必要な場合は、自分自身を参照する1つのテーブルを作成できます。一部のRDMSはこの概念をサポートしています。たとえば、SQL Serverを使用すると、階層クエリで共通テーブル式(CTE)を使用できます。

あなたの場合、投稿はレベル0になり、すべてのコメントはレベル1になります。

その他のオプションは、2つのクエリまたは結合(返されるすべてのレコードのいくつかの追加情報を含む)です(他の人が言及したもの)。

階層の例:

https://stackoverflow.com/questions/14274942/sql-server-cte-and-recursion-example

上記のリンクで、EmpLevelはネスト(または階層)のレベルを示します。

1
Jon Raynor

申し訳ありませんが、問題を正確に理解できているかどうかわかりません。

MSSQLでは、2つのSQLステートメントを実行できます。

SELECT id, content
FROM posts
WHERE id = 7

SELECT * FROM comments WHERE post_id = 7

そして、2つの結果セットを同時に返します。

0
Biff MaGriff

私の意見では、これは主にSQLと集計クエリの実行方法によるものです。集計関数とグループ化は、大きな2次元の行セットで実行され、結果を返します。これが最初からの方法であり、非常に高速です(ほとんどのNoSQLソリューションは集計が非常に遅く、複雑なクエリではなく非正規化スキーマに依存しています)。

もちろん、PostgreSQLにはオブジェクト指向データベースの機能がいくつかあります。このメール( message )によると、カスタム集計を作成することで必要なものを達成できます。

個人的には、Doctrine ORM(PHP))のようなフレームワークを使用しています。これは、アプリケーション側で集約を行い、遅延読み込みなどの機能をサポートしてパフォーマンスを向上させます。

0
Daimon

RDBMは理論に基づいており、理論に固執します。これにより、Niceの一貫性と数学的に証明された信頼性が得られます。

モデルはシンプルで、理論に基づいているため、最適化と多くの実装を簡単に行うことができます。これは、誰もがわずかに異なるNoSQLとは異なります。

過去に階層型データベースを作成する試みが行われましたが、IIRC(Googleはそれをグーグル化しているようです)には問題があります(サイクルと平等が頭に浮かびます)。

0
Adam Gent

PostgreSQLは Arrays[〜#〜] json [〜#〜] など、さまざまな構造化データタイプをサポートしています。 SQLまたは組み込み手続き言語の1つを使用して、任意に複雑な構造の値を作成し、それらをアプリケーションに返すことができます。構造化タイプの列を持つテーブルを作成することもできますが、設計を不必要に非正規化していないかどうかを慎重に検討する必要があります。

0
Jonathan Rogers

特定のニーズがあります。必要な形式でデータベースからデータを抽出して、必要な形式でデータを処理できるようにすることをお勧めします。

データベースが機能しないものもありますが、とにかくデータベースを構築することは不可能ではありません。他のアプリケーションにフォーマットを残すことは現在の推奨事項ですが、それができない理由を正当化するものではありません。

私があなたの提案に反対する唯一の議論は、この結果セットを「sql」の方法で処理できることです。データベースで結果を作成し、それを操作したり、ある程度操作したりできないようにするのは悪い考えです。あなたが提案した方法で構築されたビューを作成したとしましょう。別のselectステートメントにどのように含めるのですか?データベースは、結果を取得し、それを使って物事を行うことを好みます。どのようにして別のテーブルに結合しますか?結果セットを別のセットとどのように比較しますか?

次に、RDMSの利点はSQLの柔軟性です。テーブルからデータを選択するための構文は、システム内のユーザーまたは他のオブジェクトのリストにかなり近いです(少なくともそれが目標です)。完全に別のことをすることに意味があるかどうかはわかりません。彼らは、手続き型のコード/カーソルまたはデータのBLOBを非常に効率的に処理できるようにさえしていません。

0
JeffO