Joel Spolskyがポッドキャスト 014で、外部キーをほとんど使用したことがないと述べたのを聞いたのを覚えています (私の記憶が正しければ)。ただし、データベース全体での重複とその後のデータ整合性の問題を回避するために、それらは非常に重要に思えます。
人々はその理由について確固たる理由を持っていますか (Stack Overflow の原則に沿った議論を避けるため)?
Joel Spolskyがポッドキャスト 014で、外部キーをほとんど使用したことがないと述べたのを聞いたのを覚えています (私の記憶が正しければ)。ただし、データベース全体での重複とその後のデータ整合性の問題を回避するために、それらは非常に重要に思えます。
人々はその理由について確固たる理由を持っていますか (Stack Overflow の原則に沿った議論を避けるため)?
外部キーを使用する理由:
外部キーを使用しない理由:
ほとんどの確立されたデータベースは、強制されない外部キーを指定する方法を提供し、単なるメタデータであると思います(私にはわかりません!)。非施行はFKを使用しないすべての理由を一掃するため、2番目のセクションの理由のいずれかが当てはまる場合は、おそらくそのルートに進む必要があります。
これは育て方の問題です。教育または職業上のキャリアのどこかで、データベースの提供と管理に時間を費やした場合 (またはデータベースを使用した才能のある人々と緊密に協力した場合)、エンティティと関係の基本的な信条は、あなたの思考プロセスに深く根付いています。これらの基礎の中には、データベースでキーを指定する方法/時期/理由 (プライマリ、外部、およびおそらく代替) があります。それは第二の性質です。
ただし、過去に RDBMS 関連の取り組みでこれほど徹底した、または前向きな経験をしたことがない場合は、そのような情報にさらされていない可能性があります。あるいは、あなたの過去には、大声でアンチデータベースの環境に没頭したことがあるかもしれません (例えば、「あの DBA はばかだ - 私たちの数は少ない。私たちが選んだ Java/C# コードスリンガーは、その日を救うだろう」)。 FK (および FK が暗示する制約) は、耳を傾けるだけなら本当に重要であると言っているドウィーブの難解なせせらぎに。
ほとんどの人は子供の頃、歯を磨くことが重要だと教えられました。それなしでやっていけますか?確かに、しかしどこかで、毎食後にブラッシングした場合よりも利用できる歯が少なくなります. お母さんとお父さんがデータベースの設計と口腔衛生をカバーするのに十分な責任を負っていたら、この会話はありませんでした. :-)
それを回避できるアプリケーションはたくさんあると思いますが、それは最善のアイデアではありません。アプリケーションが常にデータベースを適切に管理できるとは限りません。率直に言って、データベースの管理はアプリケーションにとってあまり重要ではありません。
リレーショナルデータベースを使用している場合は、いくつかの関係を定義する必要があるようです。残念ながら、この態度 (外部キーは必要ありません) は、多くのアプリケーション開発者に受け入れられているようです。アプリケーション開発者は、データの整合性などのばかげたことに煩わされることはありません (ただし、会社には専任のデータベース開発者がいないため、気にする必要があります)。通常、これらのタイプでまとめられたデータベースでは、主キーを持っているだけで幸運です ;)
外部キーは、どのリレーショナル データベース モデルにも不可欠です。
私はいつもそれらを使用していますが、金融システムのデータベースを作成しています。データベースは、アプリケーションの重要な部分です。金融データベースのデータが完全に正確でない場合、コード/フロントエンドの設計にどれだけの労力を費やしても、実際には問題になりません。あなたはただ時間を無駄にしています。
また、データを読み取るだけの他のシステム (Crystal Reports) から、データを挿入するシステム (必ずしも私が設計した API を使用する必要はありません。 VBScript を発見したばかりで、SQL ボックスの SA パスワードを持っている愚かな管理者)。データベースが可能な限りばかではない場合は、さようならデータベースです。
データが重要な場合は、そうです。外部キーを使用し、一連のストアド プロシージャを作成してデータを操作し、できる限り頑丈な DB を作成します。データが重要でない場合、そもそもデータベースを作成する理由は何ですか?
更新:私は常に外部キーを使用しています。「複雑なテスト」という反対意見に対する私の答えは、「データベースをまったく必要としないように単体テストを作成することです。データベースを使用するテストでは、データベースを適切に使用する必要があり、外部キーも含まれます。セットアップが面倒な場合は、セットアップを行うためのより簡単な方法を見つけてください。」
外部キーを使用しているとします。「金融口座を更新すると、取引の記録が保存されるはずです」という自動テストを作成しています。このテストでは、2つのテーブルのみに関心があります:accounts
とtransactions
。
ただし、accounts
に外部キーがcontracts
あり、contracts
にfkがありclients
、clients
にfkがありcities
、cities
にfkがありstates
ます。
これで、データベースでは、テストに関係のない4つのテーブルにデータを設定せずにテストを実行することはできなくなります。
これには、少なくとも2つの考えられる視点があります。
テストの実行中に外部キーチェックを一時的にオフにすることも可能です。MySQLは、少なくともこれをサポートしています。
「レコードの削除がより面倒になる可能性があります。外部キーがその制約に違反する他のテーブルにレコードがある「マスター」レコードを削除することはできません。」
SQL 標準では、外部キーが削除または更新されたときに実行されるアクションが定義されていることに注意してください。私が知っているものは次のとおりです。
ON DELETE RESTRICT
- この列にキーを持つ他のテーブルの行が削除されないようにします。これは、ケン・レイが上で説明したものです。ON DELETE CASCADE
- 他のテーブルの行が削除された場合は、このテーブルでそれを参照しているすべての行を削除します。ON DELETE SET DEFAULT
- 他のテーブルの行が削除された場合、それを参照する外部キーを列のデフォルトに設定します。ON DELETE SET NULL
- 他のテーブルの行が削除された場合、このテーブルでそれを参照しているすべての外部キーを null に設定します。ON DELETE NO ACTION
- この外部キーは、それが外部キーであることを示すだけです。つまり、OR マッパーで使用します。これらの同じアクションは、 にも適用されON UPDATE
ます。
デフォルトは、使用しているSQLサーバーによって異なるようです。
@imphasing - これはまさに、メンテナンスの悪夢を引き起こす一種の考え方です。
せいぜい弱い予防手段であるいわゆる「ソフトウェア強制」を支持して、データが少なくとも一貫性があることを保証できる宣言的な参照整合性を無視するのはなぜですか。
それらを使用しない正当な理由が 1 つあり ます。それらの役割や使用方法を理解していない場合です。
間違った状況では、外部キーの制約により、偶発的なウォーターフォールの複製が発生する可能性があります。誰かが間違ったレコードを削除した場合、それを元に戻すのは膨大な作業になる可能性があります。
また、逆に、何かを削除する必要がある場合、設計が不十分な場合、制約によってあらゆる種類のロックが発生し、それが妨げられる可能性があります。
それらを使用しない正当な理由はありません...孤立した行があなたにとって大したことではない場合を除きます。
私の経験から、データベースの重要なアプリケーションでは FK を使用しない方が常に良いと言えます。ここで、FK は優れたプラクティスであるが、データベースが巨大で 1 秒あたりの CRUD 操作が膨大な場合は実用的ではないと言う人たちに異論はありません。名前を付けずに共有できます... の最大の投資銀行の 1 つは、データベースに単一の FK を持っていません。これらの制約は、プログラマーが DB を含むアプリケーションを作成する際に処理します。基本的な理由は、新しい CRUD が実行されるたびに、複数のテーブルに影響を与え、挿入/更新ごとに検証する必要があるためです。これは、単一の行に影響するクエリでは大きな問題にはなりませんが、対処するときに大きなレイテンシが発生します。大手銀行が日常業務として行うバッチ処理。
FK は避けたほうがよいですが、そのリスクはプログラマーが処理する必要があります。
より大きな質問は、目隠しをして運転しますか? 参照制約のないシステムを開発する場合は、このようになります。ビジネス要件の変更、アプリケーション設計の変更、コード内のそれぞれの論理的仮定の変更、ロジック自体のリファクタリングなどがあることに注意してください。一般に、データベースの制約は、特定の一連の論理的アサーションと仮定に対して一見正しいように見える、現代の論理的仮定の下で設定されます。
アプリケーションのライフサイクルを通じて、特に新しい要件によって論理的なアプリケーションの変更が行われる場合、参照およびデータ チェックの制約により、アプリケーションを介したデータ収集が規制されます。
このリストの主題に対して- 外部キーはそれ自体で「パフォーマンスを向上させる」ことも、リアルタイム トランザクション処理システムの観点から「パフォーマンスを大幅に低下させる」こともありません。ただし、大量の「バッチ」システムでは、制約チェックに総コストがかかります。リアルタイムとバッチのトランザクション プロセスの違いは次のとおりです。バッチ処理 - 順次処理されるバッチの制約チェックによって発生するコストの増加により、パフォーマンスが低下します。
適切に設計されたシステムでは、バッチを処理する「前に」データの整合性チェックが行われます (ただし、ここにもコストがかかります)。したがって、ロード時に外部キー制約のチェックは必要ありません。実際、外部キーを含むすべての制約は、バッチが処理されるまで一時的に無効にする必要があります。
QUERY PERFORMANCE - テーブルが外部キーで結合されている場合、外部キー列がインデックス化されていないことに注意してください (それぞれの主キーは定義によりインデックス化されています)。さらに言えば、外部キーにインデックスを付けることで、任意のキーにインデックスを付け、インデックス付きでテーブルを結合すると、外部キー制約のあるインデックスなしのキーで結合するのではなく、パフォーマンスが向上します。
件名を変更すると、データベースが Web サイトの表示/コンテンツのレンダリング/その他をサポートし、クリックを記録するだけの場合、すべてのテーブルに完全な制約があるデータベースは、そのような目的には過剰です。考えてみてください。ほとんどの Web サイトは、そのようなデータベースを使用していません。データが記録されているだけで参照されていない同様の要件については、制約のないインメモリ データベースを使用します。これは、データ モデルが存在しない、論理モデルは存在するが、物理データ モデルが存在しないという意味ではありません。
データの一貫性を維持するのに役立つという点で、以前の回答に同意します。ただし、数週間前に Jeff Atwoodが、正規化された一貫性のあるデータの長所と短所について論じた興味深い投稿がありました。
簡単に言えば、非正規化されたデータベースは、大量のデータを処理する場合により高速になります。アプリケーションによっては正確な一貫性を気にしないかもしれませんが、DB は気にしないので、データを扱うときはもっと注意する必要があります。
Clarify データベースは、主キーまたは外部キーを持たない商用データベースの例です。
http://www.geekinterview.com/question_details/18869
面白いことに、技術文書は、テーブルがどのように関連しているか、それらを結合するためにどの列を使用するかなどを説明するために非常に長い時間を費やしています.
つまり、明示的な宣言 (DRI) を使用してテーブルを結合することもできましたが、結合しないことを選択しました。
その結果、Clarify データベースは不整合でいっぱいになり、パフォーマンスが低下します。
しかし、削除や追加の前に関連する行をチェックするなど、参照整合性を処理するコードを書く必要がなくなり、開発者の仕事が楽になったと思います。
これが、リレーショナル データベースに外部キー制約がないことの主な利点だと思います。少なくともデビルメイケアの観点からは、開発が容易になります。
外部キーを使用するその他の理由: - データベースの再利用を促進
外部キーを使用しないその他の理由: - 再利用を減らすことで、顧客をツールに閉じ込めようとしています。
レコードの削除がより面倒になる可能性があります。外部キーがその制約に違反する他のテーブルにレコードがある「マスター」レコードを削除することはできません。トリガーを使用してカスケード削除を行うことができます。
主キーを賢明に選択しなかった場合、その値の変更はさらに複雑になります。たとえば、「顧客」テーブルの PK を人の名前として持っていて、そのキーを「注文」テーブルの FK にすると、顧客が自分の名前を変更したい場合、それは非常に面倒なことです.. . しかし、それはただのデータベース設計です。
Fireign キーを使用する利点は、想定される欠点を上回ると思います。
外部キー制約の検証には CPU 時間がかかるため、パフォーマンスを向上させるために外部キーを省略する人もいます。
私が知っているのは Oracle データベースだけで、他のデータベースは知りません。外部キーは、データの整合性を維持するために不可欠であることがわかります。データを挿入する前に、データ構造を作成し、正しく作成する必要があります。それが完了すると、すべての主キーと外部キーが作成され、作業は完了です。
意味: 孤立した行? いいえ、私の人生でそれを見たことはありません。悪いプログラマーが外部キーを忘れた場合、または別のレベルでそれを実装した場合を除きます。どちらも、Oracle のコンテキストでは、データの重複、孤立したデータ、つまりデータの破損につながる大きな間違いです。FK が強制されていないデータベースは想像できません。私にはカオスのように見えます。これは Unix の許可システムに少し似ています: 全員が root であると想像してください。混乱を考えてください。
外部キーは、主キーと同様に不可欠です。主キーを削除するとどうなるか? まあ、完全な混乱が起こるでしょう。それが何です。主キーまたは外部キーの役割をプログラミング レベルに移動することはできません。データ レベルで行う必要があります。
欠点?そのとおり !挿入時に、さらに多くのチェックが行われるためです。しかし、データの整合性がパフォーマンスよりも重要である場合、それは簡単なことではありません。Oracle でのパフォーマンスの問題は、PK および FK に付属するインデックスに関連しています。
私にとって、 ACID標準に準拠したい場合は、参照整合性を確保するために外部キーを用意することが重要です。
ここでコメントのほとんどを 2 番目に挙げる必要があります。外部キーは、データの整合性を確保するために必要な項目です。ON DELETE と ON UPDATE のさまざまなオプションを使用すると、使用に関してここで言及されている「ダウンフォール」のいくつかを回避できます。
すべてのプロジェクトの 99% で、データの整合性を確保するために FK を使用することがわかりましたが、クライアントが古いデータを保持しなければならない場合がまれにあります。しかし、とにかく有効なデータのみを取得するためのコードを書くのに多くの時間を費やしているため、無意味になります。
アプリケーションのライフサイクル全体での保守性と一貫性はどうですか? ほとんどのデータは、それを利用するアプリケーションよりも寿命が長くなります。関係とデータの整合性は非常に重要であり、次の開発チームがアプリのコードで正しく理解することを期待することはできません。自然な関係を尊重しない汚れたデータを含むデータベースで作業したことがない場合は、そうするでしょう。データの整合性の重要性は非常に明確になります。
ここで回答している人の多くは、参照制約によって実装された参照整合性の重要性に夢中になっています。参照整合性を備えた大規模なデータベースでの作業は、うまく機能しません。Oracle はカスケード削除が特に苦手なようです。私の経験則では、アプリケーションはデータベースを直接更新してはならず、ストアド プロシージャを介して更新する必要があります。これにより、コード ベースがデータベース内に保持され、データベースの整合性が維持されます。
多くのアプリケーションがデータベースにアクセスしている可能性がある場合、参照整合性制約のために問題が発生しますが、これは制御にかかっています。
アプリケーション開発者は、データベース開発者が必ずしも精通しているとは限らない非常に異なる要件を持っている可能性があるという点で、より広い問題もあります。
私が聞いた議論では、フロントエンドにはこれらのビジネス ルールが必要だというものです。そもそも制約を破る挿入を許可してはならない場合、外部キーは「不要なオーバーヘッドを追加」します。私はこれに同意しますか?いいえ、しかしそれは私がいつも聞いていることです。
編集:私の推測では、彼は外部キーではなく、外部キーの制約を概念として参照していました。
私はドミトリーの答えを繰り返します-非常によく言えます。
FK がしばしばもたらすパフォーマンスのオーバーヘッドを心配している人のために、(Oracle では) 挿入、削除、または更新中の制約検証のコスト オーバーヘッドなしで、FK 制約のクエリ オプティマイザーの利点を得ることができる方法があります。つまり、属性 RELY DISABLE NOVALIDATE を使用して FK 制約を作成します。これは、クエリ オプティマイザーが、データベースが実際に制約を適用することなく、クエリを作成するときに制約が適用されたと想定することを意味します。このような FK 制約をテーブルに入力するときは、FK 列に制約に違反するデータがないことを確実にするために、責任を負うようにここで非常に注意する必要があります。この FK 制約がオンになっているテーブルを含むクエリから、信頼できない結果が得られる可能性があります。
私は通常、データ マート スキーマの一部のテーブルでこの戦略を使用しますが、統合ステージング スキーマでは使用しません。データのコピー元のテーブルに既に同じ制約が適用されているか、ETL ルーチンが制約を適用していることを確認します。
また、ほとんどのデータベースでは外部キーが必要だと思います。唯一の欠点 (強制的な一貫性に伴うパフォーマンス ヒット以外) は、外部キーを使用すると、機能する外部キーがあることを前提としたコードを記述できることです。それは決して許されるべきではありません。
たとえば、参照先のテーブルに挿入するコードを書いてから、最初の挿入が成功したことを確認せずに、参照先のテーブルに挿入を試みる人を見てきました。後で外部キーを削除すると、データベースの一貫性が失われます。
また、更新または削除時に特定の動作を想定するオプションもありません。外部キーが存在するかどうかに関係なく、必要なことを行うにはコードを記述する必要があります。削除がカスケードされていないのにカスケードされていると想定すると、削除は失敗します。参照された列への更新が、参照している行に反映されていないのに反映されていると想定すると、更新は失敗します。コードを書く目的では、これらの機能を持っていないこともあるでしょう。
これらの機能がオンになっている場合、コードはとにかくそれらをエミュレートし、パフォーマンスが少し低下します。
要約すると.... 一貫したデータベースが必要な場合、外部キーは不可欠です。外部キーは、記述したコード内に存在または機能すると想定しないでください。
ドミトリーが言ったことを繰り返しますが、ポイントを追加します。
私は、30 以上のテーブルに大量の行セットを挿入する必要があるバッチ請求システムに取り組んでいました。データ ポンプ (Oracle) を実行することは許可されていなかったため、一括挿入を行う必要がありました。これらのテーブルには外部キーがありましたが、関係が壊れていないことは既に確認済みです。
挿入の前に、外部キー制約を無効にして、Oracle が挿入に永遠に時間がかからないようにします。挿入が成功したら、制約を再度有効にします。
PS: 多くの外部キーと 1 つのレコードの子行データを含む大規模なデータベースでは、外部キーが不適切な場合があり、カスケード削除を禁止したい場合があります。請求システムでは、カスケード削除を行うと時間がかかりすぎてデータベースに負担がかかりすぎるため、メイン ドライバー (親) テーブルのフィールドでレコードを不良としてマークするだけです。
多くのものと同様に、それはトレードオフです。データの整合性を検証する作業をどこで行うかが問題です。
(1) 外部キーを使用する (テーブルを構成するための単一ポイント、機能は既に実装され、テストされ、機能することが証明されています)
(2)データベースのユーザーに任せます(複数のユーザー/アプリが同じテーブルを更新する可能性があります。これは、潜在的な障害点が増え、テストの複雑さが増すことを意味します)。
データベースが (2) を実行する方が効率的であり、(1) を使用すると保守が容易になり、リスクが少なくなります。
データ構造設計の優れた原則の 1 つは、テーブルまたはオブジェクトのすべての属性が十分に理解された制約に従うようにすることです。これは重要です。ユーザーまたはユーザーのプログラムがデータベース内の有効なデータを信頼できる場合、不正なデータが原因でプログラムに問題が発生する可能性が低くなるからです。また、エラー状態を処理するためのコードを記述する時間が短縮され、エラー処理コードを前もって記述する可能性が高くなります。
多くの場合、これらの制約はコンパイル時に定義できます。その場合、属性が常に範囲内に収まるようにフィルターを記述できます。そうしないと、属性を保存しようとして失敗します。
ただし、多くの場合、これらの制約は実行時に変更される可能性があります。たとえば、最初は「赤」、「緑」、「青」の値をとる属性として「色」を持つ「車」テーブルがあるとします。プログラムの実行中にその初期リストに有効な色を追加することが可能であり、追加された新しい「車」は最新の色リストの任意の色を帯びることがあります。さらに、通常、この更新された色のリストは、プログラムの再起動後も存続する必要があります。
あなたの質問に答えるために、実行時に変更できるデータ制約の要件があり、それらの変更がプログラムの再起動後も存続する必要がある場合、外部キーが問題に対する最も単純で簡潔な解決策であることがわかります。開発コストは 1 つのテーブルの追加 (例: "colours"、"cars" テーブルへの外部キー制約、およびインデックス) であり、ランタイム コストは最新の色のための余分なテーブル ルックアップです。この実行時のコストは通常、インデックス作成とキャッシュによって軽減されます。
これらの要件に外部キーを使用しない場合は、リストを管理し、有効なエントリを検索し、ディスクに保存し、リストが大きい場合はデータを効率的に構造化し、リストへの更新が行われないようにするためのソフトウェアを作成する必要があります。リスト ファイルを破損させたり、複数のリーダーやライターが存在する場合にリストへのシリアル アクセスを提供したりします。つまり、多くの RDBMS 機能を実装する必要があります。
FK が問題を引き起こす可能性があるのは、キーを使用する必要がなくなったにもかかわらず、キーを参照する履歴データが (ルックアップ テーブル内に) ある場合です。
明らかに、解決策は物事を前もってより良く設計することですが、私はここで、完全な解決策を常に制御できるとは限らない現実の状況について考えています。
たとえば、customer_type
さまざまなタイプの顧客を一覧表示するルックアップ テーブルがあるとします。たとえば、特定の顧客タイプを削除する必要があるが、(業務上の制約により) クライアント ソフトウェアを更新できず、誰もこの状況に気付かなかったとします。ソフトウェアを開発するとき、それが他のテーブルの外部キーであるという事実により、行を参照する履歴データが無関係であることを知っていても、行を削除できない場合があります。
これで数回やけどを負った後、おそらく関係のデータベース強制から遠ざかります。
(これが良いと言っているのではありません。一般的に、FK と db 制約を避けることに決めた理由を示しているだけです)
私が取り組んだプロジェクトでは、多くのテーブルを同じ列で結合できるように、明示的ではなく暗黙的な関係がしばしばありました。
次の表を取る
住所
EntityType の可能な値は、Employee、Company、Customer である可能性があり、EntityId は、関心のあるテーブルの主キーを参照します。
これが最善の方法だとは思いませんが、このプロジェクトではうまくいきました。
DB2 では、MQT (Materialized Query Tables) が使用されている場合、オプティマイザーが特定のクエリに対して適切なプランを選択するには、外部キー制約が必要です。それらにはカーディナリティ情報が含まれているため、オプティマイザーはメタデータを多用して MQT を使用するかどうかを判断します。
私もこの議論を聞いたことがあります。外部キーにインデックスを付けるのを忘れて、特定の操作が遅いと不平を言った人からです (制約チェックが任意のインデックスを利用する可能性があるため)。要約すると、外部キーを使用しない正当な理由はありません。最新のデータベースはすべてカスケード削除をサポートしているため...
非常に頻繁に FK 制約でエラーを受け取ります子行 を追加または更新できません: 外部キー制約が失敗しますレコードが contract_lines に既に存在するか、ベース テーブルから PK 列を削除したい場合、FK 制約のエラーが発生します。以下に示す手順を使用して回避できます。
CREATE TABLE inventory_source (
inventory_source_id int(11) NOT NULL AUTO_INCREMENT,
display_name varchar(40) NOT NULL,
state_id int(11) NOT NULL,
PRIMARY KEY (inventory_source_id),
KEY state_id (state_id),
CONSTRAINT ba_inventory_source_state_fk FOREIGN KEY (state_id) REFERENCES ba_state (state_id)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
CREATE TABLE contract_lines(
contract_line_id int(11) NOT NULL AUTO_INCREMENT,
inventory_source_id int(11) NULL ,
PRIMARY KEY (contract_line_id),
UNIQUE KEY contract_line_id (contract_line_id),
KEY AI_contract_line_id (contract_line_id),
KEY contract_lines_inventory_source_fk (inventory_source_id),
CONSTRAINT contract_lines_inventory_source_fk FOREIGN KEY (inventory_source_id) REFERENCES ba_inventory_source (inventory_source_id)
) ENGINE=InnoDB AUTO_INCREMENT=135 DEFAULT CHARSET=utf8 ;
次の手順を使用してそれを克服できます。
私はいつもそれらを使わないのは怠け者だと思っていました. 必ずやるべきだと教わりました。しかし、その後、私はジョエルの議論を聞きませんでした. 彼には正当な理由があったのかもしれませんが、私にはわかりません。
外部キーを使用する理由はいくつかありますが (誰かが言ったように、孤立した行は面倒です)、私もそれらを使用したことはありません。比較的健全な DB スキーマでは、それらが 100% 必要だとは思いません。制約は良いですが、ソフトウェアを介して強制する方が良い方法だと思います。
アレックス