2017-06-06 74 views
1

因此,我必须创建一个存储过程来在Oracle DataBase中搜索电影。在PL/SQL测试LOOP中替换DBMS_OUPUT以返回集合

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

  1. 它看起来对电影的括号
    防爆年。 (1992)

  2. 它在括号内寻找一年的范围
    Ex。 [1992,2000]

  3. 它寻找包含在标题,国家,realisator,流派,演员或演员的单词。

  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的问题指出,应该避免。

  • 使用管道行缝合的问题只能与函数一起工作(更改过程调用的函数的过程可能是一种有效的解决方法,我想知道在tho之前是否还有其他可行的方法)。

+0

这些条件应该是OR还是AND?因为您应该能够编写一个查询,以便为您提供符合这些条件的所有内容。 – SandPiper

+0

他们是AND条件,我很乐意在单个查询中做到这一点,你能告诉我一个例子吗?例如:“电影标题”和“演员1”和“演员2”和“年份”(订单可以更改) – 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; 

你的参数“电影”拥有所有从你的程序产生的积极成果,以及所有你需要做的就是读取休眠光标。

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

+0

这工作得很好,我改变了提交保存的提交删除,所以我不必关闭每次会话。我正在使用session.createStoredProcedureQuery()。你知道如果hibernate处理关闭光标吗? – user3220633

+0

Hello @ user3220633很高兴听到它对您有用,如果您更改了ON COMMIT行为,请记住取下我在您的过程中植入的“提交”。 我很抱歉地说我不熟悉Hibernate,就我所见到的其他语言(即PHP,C#)而言,关闭连接会释放服务器端资源(包括游标),但不要采用我的话,如果我们在这个问题上进行一些探索,会发现不好的事情会发生。 –

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,标题和年份是因为标题和年份将与film_id有不同的值。有几种方法可以对这只猫进行皮肤处理,但它都是围绕使用LIKE语句和您定义的变量进行的。如果您不想明确定义脚本中的值,则可以使用ACCEPT命令。或者,您可以将它与其他将这些值应用于变量的其他接口相连接。

有了这种方法,在你如何确定你的数据的布局方面,这在黑暗中是一种镜头。但要记住良好的关系数据库管理实践,你应该能够把它变成一个可用的查询。

其他方法:

您还可以使用一系列的子查询发现上面用类似的语句相同的数据,然后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 

那么你将有得到

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

然后,INNER JOIN会给你的ID号码2和3,因为这些都是所有的条件得到满足的唯一部分。重复您的其他搜索条件的过程。我希望这有帮助。

+0

与此问题是,它不处理“递归”wheres,并且没有办法确定在哪里拆分参数。对于每个参数,字符串可以包含从0到X的任何数字。例如:可能有2个演员1个国家和3个流派。然而,没有办法告诉我们:第一个是演员,第三个是国家,其余的都是流派。可以将所有的字符串连接起来,并使用像我一样的地方,然后测试每个单词,但那是我不适合查询的部分。你知道在每个子字符串的哪里做一个foreach的方法吗? – user3220633

+0

这很有趣,但这仍然不能解决主要问题。谢谢你的时间寿! – user3220633