在PL / SQL测试LOOP中替换DBMS_OUPUT以返回集合

时间:2017-06-06 18:09:28

标签: oracle hibernate plsql dbms-output

所以我有这个任务,我必须创建一个存储过程来搜索Oracle DataBase中的电影。

搜索字符串遵循以下逻辑:

  1. 在括号中查找电影的年份
    防爆。 (1992)

  2. 它在括号中查找一年的范围 防爆。 [1992,2000]

  3. 它会查找标题,国家/地区,演员,流派,演员或剧情中的单词。

  4. 上述任何一种都可以组合多次。
    防爆。 :指环王伊恩麦克莱恩克里斯托弗李[1992,2000]

  5. 用于解决此问题的逻辑是进行巨型查询以对所有必需数据进行分组,然后使用游标通过游标循环结果集以检查搜索字符串中的每个单词是否有效。

    我设法制作了一个按预期工作的程序,但我发现返回结果的唯一方法是使用DBMS_OUTPUT。现在的问题是,当我插入这是Hibernate时,DBMS_OUTPUT不会被发送到客户端。我通过设置DBMS_OUTPUT.enable来读取强制输出的方法,但我觉得这不是正确的方法。

    所以这是我的问题:

    1. 我的逻辑有缺陷吗?是否有更简单的方法可以使用单个选择或其他内容存档?

    2. 有没有办法动态推送游标内的数据并将其返回?

    3. 我真的应该欺骗DBMS_OUTPUT,以便将其发送到休眠状态吗?

    4. 这是我的代码:

      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的问题表明应该避免这种情况。

      • 使用管道行搜索的问题只能与函数一起使用(更改程序调用的函数的过程可能是一个有效的解决方法,我想知道是否还有其他内容可以使用)。

2 个答案:

答案 0 :(得分: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;

现在你的参数&#39;电影&#39;得到了你的程序产生的所有积极结果,所有你需要做的就是在Hibernate中读取光标。

请注意,一旦关闭连接,临时表将丢失所有数据,并且可以在另一个会话开始时再次使用(始终记得关闭游标/连接)。

答案 1 :(得分: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的原因是因为标题和年份将是film_id的不同值。有两种方法可以对这只猫进行修饰,但这一切都围绕着使用LIKE语句和你定义的变量。如果您不想在脚本中显式定义值,则可以使用ACCEPT命令。或者,您可以将其与将这些值应用于变量的其他内容进行交互。

通过这种方法,您可以在黑暗中了解如何确定数据布局。但请记住良好的关系数据库管理实践,您应该能够将其转换为可用的查询。

其他方法:

您也可以使用一系列子查询使用like语句查找上面相同的数据,然后将它们连接在一起以确定哪些是共同的。例如:

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

然后在第二个子查询中,如果你搜索了'Donald Glover',你就会得到

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

然后,INNER JOIN会给你ID号2和3,因为那些是唯一满足所有标准的。对您的其他搜索字词重复此过程。我希望这会有所帮助。