所以我有这个任务,我必须创建一个存储过程来搜索Oracle DataBase中的电影。
搜索字符串遵循以下逻辑:
在括号中查找电影的年份
防爆。 (1992)
它在括号中查找一年的范围 防爆。 [1992,2000]
它会查找标题,国家/地区,演员,流派,演员或剧情中的单词。
上述任何一种都可以组合多次。
防爆。 :指环王伊恩麦克莱恩克里斯托弗李[1992,2000]
用于解决此问题的逻辑是进行巨型查询以对所有必需数据进行分组,然后使用游标通过游标循环结果集以检查搜索字符串中的每个单词是否有效。
我设法制作了一个按预期工作的程序,但我发现返回结果的唯一方法是使用DBMS_OUTPUT。现在的问题是,当我插入这是Hibernate时,DBMS_OUTPUT不会被发送到客户端。我通过设置DBMS_OUTPUT.enable来读取强制输出的方法,但我觉得这不是正确的方法。
所以这是我的问题:
我的逻辑有缺陷吗?是否有更简单的方法可以使用单个选择或其他内容存档?
有没有办法动态推送游标内的数据并将其返回?
我真的应该欺骗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 :(得分: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,因为那些是唯一满足所有标准的。对您的其他搜索字词重复此过程。我希望这会有所帮助。