1

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

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


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

  2. 它在括号中查找年份范围,
    例如。[1992,2000]

  3. 它查找包含在标题、国家/地区、实现者、流派、演员或编剧中的单词。

  4. 以上任何一种都可以多次组合。
    前任。: 指环王伊恩麦克莱恩克里斯托弗李 [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 的问题表明应该避免。

  • 使用管道行缝合的问题仅适用于函数(更改由过程调用的函数的过程可能是一种有效的解决方法,我想知道在此之前是否还有其他可能)。

4

2 回答 2

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 中读取光标。

请注意,一旦您关闭连接,临时表就会丢失所有数据,并准备好在另一个会话开始时再次使用(始终记住关闭您的游标/连接)。

于 2017-06-06T21:40:05.693 回答
0

第一部分可能只需一个查询即可完成。您可以像这样定义您的搜索词:如何声明变量并在同一个 SQL 脚本中使用它?(甲骨文 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命令。或者,您可以将它与将这些值应用到变量中的其他东西连接起来。

使用这种方法,对于您如何真正布置数据有点摸不着头脑。但请记住良好的关系数据库管理实践,您应该能够将其转化为可用的查询。

附加方法:

您还可以使用一系列子查询,使用 like 语句查找上面相同的数据,然后将它们 INNER JOIN 一起找出哪些是共同的。例子:

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,因为它们是唯一满足所有条件的 ID。对其他搜索词重复此过程。我希望这有帮助。

于 2017-06-06T23:51:29.670 回答