2017-06-06 9 views
1

この割り当ては、Oracle DataBaseでムービーを検索するためのストアド・プロシージャを作成する必要があります。PL/SQLテストLOOPでDBMS_OUPUTを置換してコレクションを戻す

検索文字列は、次のロジックに従います。

  1. それはカッコ
    例では、映画の年を探します。 (1992)

  2. 括弧内に年の範囲を探します。
    Ex。 [1992,2000]

  3. タイトル、国、実在主体、ジャンル、俳優または脚本家に含まれる単語を検索します。

  4. 上記のいずれかを複数回組み合わせることができます。
    Ex。 :リングイアン・マッケランクリストファー・リー[1992,2000]

の主は、この問題を解決するために使用されるロジックは、必要なすべてのデータは、結果をループにカーソルを使用するグループに巨大なクエリを作ることでしたカーソルで設定して、検索文字列のすべての単語が有効かどうかを確認します。

私は期待通りに動作するプロシージャを作成することができましたが、結果を返すために見つかった唯一の方法はDBMS_OUTPUTを使用することでした。今問題は、これをHibernateにプラグすると、DBMS_OUTPUTがクライアントに送信されないということです。 DBMS_OUTPUT.enableを設定して出力を強制する方法をいくつか読んだが、これは適切な方法ではないと感じている。だからここ

は私の質問です:私のロジックは

  1. 欠陥がありますか?単一の選択肢でこれをアーカイブする簡単な方法はありますか?

  2. カーソル内のデータを動的にプッシュして戻す方法はありますか?

  3. 私は本当にDBMS_OUTPUTをトリックして、休止状態に送信されるはずですか?

    CREATE OR REPLACE PROCEDURE p_SearchFilm(searchString IN VARCHAR2) IS 
        IsValid BOOLEAN; 
        y1 INTEGER; 
        y2 INTEGER; 
        subStrArray apex_application_global.vc_arr2; 
        term VARCHAR(100); 
    
        CURSOR films IS 
          Select FilmId, Titre, real.Prenom||' '||real.nom as Realisateur, anneeSortie, ListPays, ListGenres, 
            ListScenaristes, ListActeurs, langueOrigine 
           from Film 
           natural left join 
            (select FilmId, listagg(p.Nom, ',') within group (Order By p.nom) ListPays from Film 
             natural join Film_vs_pays 
             natural join Pays p 
             Group by FilmId) 
           natural left join 
            (select FilmId, listagg(g.Nom, ',') within group (Order By g.nom) ListGenres from Film 
             natural join Film_vs_Genre 
             natural join Genre g 
             Group by FilmId) 
           natural left join 
            (select FilmId, listagg(p.Prenom||' '||p.Nom, ',') within group (Order By p.nom) ListScenaristes from Film 
             natural join Scenariste s 
             join Personne p on s.personneId = p.personneId 
             Group by FilmId) 
           natural left join 
            (select FilmId, listagg(p.Prenom||' '||p.Nom, ',') within group (Order By p.nom) ListActeurs from Film 
             natural join Personnage perso 
             join Personne p on perso.personneId = p.personneId 
             Group by FilmId) 
           left join Personne real on real.personneId = realisateurId; 
    BEGIN 
        <<FILM_LOOP>> 
        FOR film IN films LOOP 
         subStrArray := apex_util.string_to_table(searchString, ' '); 
         FOR i in 1..subStrArray.count LOOP 
          IsValid:= FALSE; 
          term:= subStrArray(i); 
          IF REGEXP_LIKE(term, '\(\d{4}\)') THEN 
           IF film.anneeSortie = TO_NUMBER(regexp_substr(term, '\d{4}')) THEN 
            IsValid:= TRUE; 
           END IF; 
          ELSIF REGEXP_LIKE(term, '\[\d{4},\d{4}\]') THEN 
           y1:= regexp_substr(term, '\d{4}', 1, 1); 
           y2:= regexp_substr(term, '\d{4}', 1, 2); 
    
           IF film.anneeSortie BETWEEN y1 AND y2 THEN 
            IsValid:= TRUE; 
           END IF; 
          ELSE 
           IF UPPER(film.Titre||film.Realisateur||film.ListActeurs||film.ListScenaristes||film.ListGenres||film.ListPays||film.langueOrigine) 
            LIKE '%'||UPPER(term)||'%' THEN 
            IsValid:= TRUE; 
           END IF; 
          END IF; 
    
          IF NOT IsValid THEN 
           CONTINUE FILM_LOOP; 
          END IF; 
    
         END LOOP; 
    
         DBMS_OUTPUT.put_line(film.FilmId||'|'||film.Titre); 
        END LOOP; 
    END; 
    

    ここに小さな免責事項:私はこの問題に対処し、いくつかの類似した質問が、カーソルを使用したものを見た

    • が完全に戻った。ここ

は私のコードです選択した行を手作業ではなく選択します。

  • DBMS_OUTPUTとHibernateについての質問は避けるべきだと述べました。

  • 機能でのみ動作するようにパイプの列を使用して質問しています(プロシージャで呼び出された関数のプロシージャを変更すると有効な回避策になる可能性があります。

  • +0

    これらの条件は、ORまたはANDである必要がありますか?これらの基準を満たすすべてのものを提供するクエリを書くことができるはずです。 – SandPiper

    +0

    これはAND条件であり、これを1回のクエリで実行したいと思っています。例: "映画タイトル" AND "Actor 1" AND "Actor 2" AND "Year range"(注文は変更可能) – user3220633

    答えて

    0

    DBMS_OUTPUTパッケージを使用することはほとんど無名ブロックの開発の実行に囲まれ、したがって、Hibernateフレームワークを使用して意図した通信に適していません。

    すでにあなたのフィルタを適用し、あなたの肯定的な結果を決定するために、ストアドプロシージャを使用している場合は、ソリューションは、これらの陽性で一時テーブルを移入し、その後だけが一時的にデータを持つことになります開いているカーソルを返す可能性がありテーブルには、のようなもの:もちろん

    create global temporary table movie_results(movie varchar2(200)) on commit preserve rows; 
    

    あなたの一時テーブルは、多くの列を持っていますが、私はちょうど私の解決策を説明するために、このようにそれを残すせることができます。

    CREATE OR REPLACE PROCEDURE p_SearchFilm(searchString IN VARCHAR2, movies out SYS_REFCURSOR) IS 
         IsValid BOOLEAN; 
         y1 INTEGER; 
         y2 INTEGER; 
         subStrArray apex_application_global.vc_arr2; 
         term VARCHAR(100); 
    
         CURSOR films IS 
           Select FilmId, Titre, real.Prenom||' '||real.nom as Realisateur, anneeSortie, ListPays, ListGenres, 
             ListScenaristes, ListActeurs, langueOrigine 
            from Film 
            natural left join 
             (select FilmId, listagg(p.Nom, ',') within group (Order By p.nom) ListPays from Film 
              natural join Film_vs_pays 
              natural join Pays p 
              Group by FilmId) 
            natural left join 
             (select FilmId, listagg(g.Nom, ',') within group (Order By g.nom) ListGenres from Film 
              natural join Film_vs_Genre 
              natural join Genre g 
              Group by FilmId) 
            natural left join 
             (select FilmId, listagg(p.Prenom||' '||p.Nom, ',') within group (Order By p.nom) ListScenaristes from Film 
              natural join Scenariste s 
              join Personne p on s.personneId = p.personneId 
              Group by FilmId) 
            natural left join 
             (select FilmId, listagg(p.Prenom||' '||p.Nom, ',') within group (Order By p.nom) ListActeurs from Film 
              natural join Personnage perso 
              join Personne p on perso.personneId = p.personneId 
              Group by FilmId) 
            left join Personne real on real.personneId = realisateurId; 
        BEGIN 
         <<FILM_LOOP>> 
         FOR film IN films LOOP 
          subStrArray := apex_util.string_to_table(searchString, ' '); 
          FOR i in 1..subStrArray.count LOOP 
           IsValid:= FALSE; 
           term:= subStrArray(i); 
           IF REGEXP_LIKE(term, '\(\d{4}\)') THEN 
            IF film.anneeSortie = TO_NUMBER(regexp_substr(term, '\d{4}')) THEN 
             IsValid:= TRUE; 
            END IF; 
           ELSIF REGEXP_LIKE(term, '\[\d{4},\d{4}\]') THEN 
            y1:= regexp_substr(term, '\d{4}', 1, 1); 
            y2:= regexp_substr(term, '\d{4}', 1, 2); 
    
            IF film.anneeSortie BETWEEN y1 AND y2 THEN 
             IsValid:= TRUE; 
            END IF; 
           ELSE 
            IF UPPER(film.Titre||film.Realisateur||film.ListActeurs||film.ListScenaristes||film.ListGenres||film.ListPays||film.langueOrigine) 
             LIKE '%'||UPPER(term)||'%' THEN 
             IsValid:= TRUE; 
            END IF; 
           END IF; 
    
           IF NOT IsValid THEN 
            CONTINUE FILM_LOOP; 
           END IF; 
    
          END LOOP; 
    
          --DBMS_OUTPUT.put_line(film.FilmId||'|'||film.Titre); 
          insert into movie_results(movie) 
          values film.FilmId||'|'||film.Titre; 
    
          commit; 
         END LOOP; 
    
         open movies for 
          select * 
          from movie_results; 
        END; 
    

    パラメータ 'movies'には、手続きから派生した肯定的な結果がすべてあります。これは、Hibernateのカーソルを読み取るだけです。

    一度接続を閉じると、一時テーブルはすべてのデータを失い、別のセッションが開始されたときにすぐに使用できるようになります(カーソル/接続を閉じてください)。

    +0

    これはかなりうまくいきます。毎回セッション。私はsession.createStoredProcedureQuery()を使用しています。休止状態がカーソルを閉じるのを処理するかどうか知っていますか? – user3220633

    +0

    こんにちは@ user3220633それがあなたのために働いてくれてうれしいです。あなたがON COMMITの振る舞いを変えたら、あなたの手順に植えた「コミット」を外してください。 他の言語(PHP、C#)で見た限り、私はHibernateに慣れていないと申し訳ありませんが、接続を閉じるとカーソルを含むサーバー側のリソースが解放されますが、それについての私の言葉は、私が主題について少しを検索すれば悪いことに気付くでしょう。 –

    0

    これの最初の部分はおそらく単なるクエリで達成できます。あなたはこのような検索用語を定義することができます:How to declare variable and use it in the same SQL script? (Oracle SQL)

    を私は(例えば、適切なデータを参加)あなたに基本的なクエリの書き込みを残しておきますが、あなたのスクリプトが有効に次のようになります:

    DEFINE var_year1 = 1992; 
    DEFINE var_year2 = 1994; 
    DEFINE var_country = null; 
    DEFINE var_title = 'Lord Of The Rings'; 
    DEFINE var_realisator = null; 
    DEFINE var_genre = null; 
    DEFINE var_actors = 'Ian'; 
    DEFINE var_scenarists = 'Lee'; 
    
    SELECT film_id, title, year 
    FROM ... 
    WHERE year BETWEEN &var_year1 AND &var_year2 
    AND UPPER(title) LIKE UPPER('%&var_title%') 
    AND UPPER(country) LIKE UPPER('%&var_country%') 
    AND UPPER(realisator) LIKE UPPER('%&var_realisator%') 
    AND UPPER(genre) LIKE UPPER('%&var_genre%') 
    AND UPPER(actors) LIKE UPPER('%&var_actors%') 
    AND UPPER(scenarists) LIKE UPPER('%&var_scenarists%'); 
    /
    

    理由私はfilm_id、title、yearを選択するのは、titleとyearがfilm_idとは異なる値になるからです。この猫をスキンするにはいくつかの方法がありますが、あなたが定義した変数を含むLIKE文を使用しています。スクリプト内の値を明示的に定義しない場合は、ACCEPTコマンドを使用できます。あるいは、変数にそれらの値を適用する他のものとインターフェイスすることができます。

    このアプローチでは、データを実際にどのようにレイアウトしているか、暗闇の中で一種のショットでした。しかし、良いリレーショナルデータベース管理方法を念頭に置いて、これを有効なクエリに変えることができます。

    追加方法:

    あなたはまた、のようなステートメントを使用して上記と同じデータを見つけるために、サブクエリのシリーズを使用することができ、その後、INNERは共通であるものを把握するためにそれらを一緒に参加します。例:あなたはそれで「星の戦争」の検索した場合

    WITH sub_title AS (SELECT film_id FROM Film WHERE UPPER(title) LIKE UPPER('%&var_title%')), 
    ... 
    sub_actor AS (SELECT film_id FROM (...) WHERE UPPER(actor) LIKE UPPER('%&var_actor%')) 
    SELECT film_id 
    FROM sub_title t 
        INNER JOIN sub_actor a ON a.film_id = t.film_id; 
    /
    

    、あなたは「ドナルド・グローバー」で検索した場合、第二副問合せにあなたが持っているでしょう例えば

    1 Star Wars A New Hope 
    2 Star Wars The Empire Strikes Back 
    3 Star Wars Return of the Jedi 
    

    を返しただろう

    2 Star Wars The Empire Strikes Back 
    3 Star Wars Return of the Jedi 
    

    得てその後、INNERはそれらが基準のすべてが満たされただけのものだったので、あなたのID番号2と3を与えているだろう登録しよう。あなたの他の検索条件についても同じ手順を繰り返します。私はこれが役立つことを願っています

    +0

    この問題は、 "再帰的な" wheresを処理しないため、パラメータをどこで分割するかを決める方法がないことです。文字列には、各パラメータの0〜Xの任意の数値を含めることができます。例:1つの国と3つのジャンルの2人の俳優がいる可能性があります。しかし、最初の2人は俳優、3人は国、残りはジャンルです。すべての文字列の仲介者を連結し、私がしたような場所を使用し、すべての単語をテストすることは可能ですが、その部分はクエリに収まりませんでした。各部分文字列のどこでforeachを行う方法を知っていますか? – user3220633

    +0

    これは興味深いですが、これはまだ主要な問題を解決しません。あなたの時間のためにありがとうtho! – user3220633

    関連する問題