3

Oracle9i のデータベースに次のテーブルがあります。

CREATE TABLE involved_in
(
    rid number,
    aid number NOT NULL,
    fid number NOT NULL,
    role varchar(80),
    note varchar(80), 
    job varchar(35),
    PRIMARY KEY(rid),
    FOREIGN KEY(fid) REFERENCES production(pr_id),
    FOREIGN KEY(aid) REFERENCES person(pid)
);

映画 (fid) で働いた俳優 (援助) に関するデータが含まれています。

私がやりたいことは、 ベーコン数を計算することに似ています

私のケビンベーコンを除いて、援助517635を持っていることで知られています.

517635 への接続を見つけるために、特定のアクターを (別の支援によって) 接続する必要があるアクターの数を (SQL ステートメントのみを使用して) 計算する方法についての手がかりがありません。

クエリの結果は、指定されたアクターが私の男に到達するために接続する必要があるすべてのアクターのリスト、または単なる数字のいずれかになります。

そのためには、最初に 517635 が協力したすべてのアクターを取得する必要があると考えました。これらのアクターは、彼と直接協力した結果として 1 になります。テーブルは大きすぎませんが、それを不可能にするのに十分な大きさです。

例として、ブラッド・ピットが私の 517635 であるとしましょう。彼はミスター・アンド・ミセス・スミスでアンジェリーナ・ジョリーと仕事をしたとしましょう。それは彼女をナンバー 1 にするでしょう。ジョリーは彼と一緒だったので、ブラッド・ピットに対するブルース・ウィリスの番号は2になります.

私のクエリでは、指定された番号がアンジェリーナの場合、結果は次のようになります: "ブラッド ピット 1" または単に "1" 指定された番号がウィリスの場合、結果は次のようになります: "アンジェリーナ ジョリー ブラッド ピット 2" または "2" 何の例表にあります:

INSERT INTO involved_in(rid, aid, fid, role, note, job) VALUES(1, 33,                  1584953, 'Himself', 'NULL', 'actor');
INSERT INTO involved_in(rid, aid, fid, role, note, job) VALUES(2, 1135, 1999660, 'Himself', 'NULL', 'actor');
INSERT INTO involved_in(rid, aid, fid, role, note, job) VALUES(3, 1135, 2465724, 'Himself', 'NULL', 'actor');
INSERT INTO involved_in(rid, aid, fid, role, note, job) VALUES(4, 6003, 2387806, 'Himself', '(archive footage)', 'actor');
INSERT INTO involved_in(rid, aid, fid, role, note, job) VALUES(5, 13011, 1935123, 'Himself', 'NULL', 'actor');

私は何も考えていません。私はSQLにまったく慣れていないので、ループの数をカウントする変数を使用して無限ループにつながると考えることができます。どこから始めて、運が良ければ終了するかについてのアイデアはありますか?

4

2 に答える 2

2

これは、サンプル データなしで確認するのは困難ですが、次の回答は、少なくとも構文的には正しいです。

ここで必要なのは、明らかに再帰クエリです。Oracle でこれを行う方法はいくつかあります。共通テーブル式 (CTE) (つまり、with句) を使用します。またはconnect by節を使用します。CTE は SQL の標準でconnect by独自connect byのものですが、あまり混乱しないと思うので、それを使用します (また、9i では再帰的な CTE はサポートされていません)。

SELECT   aid, MIN (lvl) AS distance
FROM     (SELECT     ii2.aid, LEVEL AS lvl
          FROM       involved_in ii1
                     JOIN involved_in ii2
                        ON ii1.fid = ii2.fid AND ii1.aid <> ii2.aid
          START WITH ii1.aid = 517635
          CONNECT BY NOCYCLE ii1.aid = PRIOR ii2.aid)
GROUP BY aid
  • 結合は、a をaid共有するすべてのものfidを相互に接続します。
  • この句は、接続された の各セットを の別のセットconnect byに結合します。aidaid
  • このnocycle句は、無限再帰を防ぎます。
  • level再帰が発生した回数を示すキーワードです。与えられたものは複数のパスを介しaidて開始に接続できるため、最小値を取得する必要があります。aid
  • このクエリのパフォーマンスが非常に悪い場合は、特定の距離内でのみ結果を取得することをお勧めします。これを行う最善の方法はand level <= 100connect by節に追加することです (この場合、結果を 100 以下の距離に制限します)。

コメントで指摘されているように、9i はサポートしていませんnocycle。さらに、このクエリを実行すると、OP の一時スペースが不足します。この 2 つは実際には無関係です。サイクルが発生すると、Oracle はエラーをスローします。これは、サイクルが見つかる前にサーバーが一時スペースを使い果たしていることを意味します。これらの問題のいずれかを回避する良い方法はありません。

終点を(ある程度)指定することが可能です。句にエンドポイントのAND ii1.aid <> 2where 2isを追加できます。これにより、クエリはその値に遭遇したときにブランチのナビゲートを停止します。ただし、これは、提供された値が見つかったブランチのみを短絡するため、上記の問題には役立たない場合があります。値がどこかにある場合に備えて、他のすべてのブランチを評価する必要があります。aidon

于 2015-05-01T14:35:05.503 に答える
1

最適な方法は、階層クエリを使用することです。

Oracle (9i バージョンでも) には CONNECT BY 句があります。http://docs.oracle.com/cd/B19306_01/server.102/b14200/queries003.htm

START WITH と LEVEL を組み合わせると、これはとてつもなく簡単になります。

例:

SELECT last_name, employee_id, manager_id, LEVEL
FROM employees
START WITH employee_id = 100
CONNECT BY PRIOR employee_id = manager_id
ORDER SIBLINGS BY last_name;
于 2015-05-01T14:34:46.863 に答える