如何在不重复的情况下重用大型查询?

时间:2012-02-02 20:26:31

标签: sql plsql dry

如果我有两个查询,我将调用horrible_query_1ugly_query_2,我想对它们执行以下两个减号操作:

(horrible_query_1) minus (ugly_query_2)
(ugly_query_2) minus (horrible_query_1)

或许我有一个terribly_large_and_useful_query,它产生的结果集我想用作未来几个查询的一部分。

如何避免在多个位置复制和粘贴相同的查询?我怎么能“不重复自己”,并遵循DRY原则。这可能在SQL中吗?

我正在使用Oracle SQL。便携式SQL解决方案是首选,但如果我必须使用Oracle特定功能(包括PL / SQL),那就没问题。

5 个答案:

答案 0 :(得分:17)

create view horrible_query_1_VIEW as 
 select .. ...
  from .. .. ..

create view ugly_query_2_VIEW as 
 select .. ...
  from .. .. ..

然后

(horrible_query_1_VIEW) minus (ugly_query_2_VIEW)

(ugly_query_2_VIEW) minus (horrible_query_1_VIEW)

或者,或许,with clause

with horrible_query_1 as (
  select .. .. ..
    from .. .. ..
) ,
ugly_query_2 as (
  select .. .. ..
     .. .. ..
)
(select * from horrible_query_1 minus select * from ugly_query_2    ) union all
(select * from ugly_query_2     minus select * from horrible_query_1)

答案 1 :(得分:4)

如果要重用查询的 SQL text ,那么定义视图是最好的方法,如前所述。

如果要重用查询的结果,则应考虑全局临时表。这些临时表存储会话或事务持续时间的数据(无论您选择哪个)。如果您需要多次重复使用计算数据,这些非常有用,特别是如果您的查询确实“丑陋”和“可怕”(意味着长时间运行)。有关详细信息,请参阅Temporary tables

如果您需要将数据保留的时间长于会话,则可以考虑具体化视图

答案 2 :(得分:1)

您是否尝试在查询中使用RESULT_CACHE提示?另外,你可以

ALTER SESSION SET RESULT_CACHE_MODE=FORCE

看看它是否有帮助。

答案 3 :(得分:1)

由于您使用的是Oracle,我将创建Pipelined TABLE函数。 该函数接受参数并返回一个对象(您必须创建) 然后使用TABLE()函数从中选择SELECT *甚至特定列,并将其与WHERE子句或JOIN一起使用。如果你想要一个重用单元(一个函数),你不仅限于返回值(即一个标量函数),你可以编写一个返回行或记录集的函数。 像这样的东西:

FUNCTION RETURN_MY_ROWS(Param1 IN type...ParamX IN Type)
            RETURN PARENT_OBJECT PIPELINED
            IS
            local_curs cursor_alias; --you need a cursor alias if this function is in a Package
            out_rec ROW_RECORD_OF_CUSTOM_OBJECT:=ROW_RECORD_OF_CUSTOM_OBJECT(NULL, NULL,NULL) --one NULL for each field in the record sub-object
        BEGIN
         OPEN local_curs FOR
          --the SELECT query that you're trying to encapsulate goes here
          -- and it can be very detailed/complex and even have WITH () etc..
        SELECT * FROM baseTable WHERE col1 = x;

   -- now that you have captured the SELECT into a Cursor
   -- here you put a LOOP to take what's in the cursor and put it in the 
   -- child object (that holds the individual records)
          LOOP
         FETCH local_curs --opening the ref-cursor
          INTO out_rec.COL1, 
               out_rec.COL2,
               out_rec.COL3;
        EXIT WHEN local_curs%NOTFOUND;
         PIPE ROW(out_rec); --piping out the Object
        END LOOP;
        CLOSE local_curs;  -- always do this
        RETURN;  -- we're now done
  END RETURN_MY_ROWS;

完成后,您可以像这样使用

SELECT * FROM TABLE(RETURN_MY_ROWS(val1, val2)); 

你可以从中插入SELECT或甚至CREATE TABLE,你可以在连接中使用它。

还有两件事需要提及:

--ROW_RECORD_OF_CUSTOM_OBJECT is something along these lines
CREATE or REPLACE TYPE ROW_RECORD_OF_CUSTOM_OBJECT AS OBJECT
(
     col1 type;
     col2 type;
      ...
     colx type;
 );

和PARENT_OBJECT是我们刚刚制作的另一个对象(带有字段定义)的表

create or replace TYPE PARENT_OBJECT IS TABLE OF ROW_RECORD_OF_CUSTOM_OBJECT;

所以这个函数需要两个OBJECT来支持它,但是一个是记录,另一个是该记录的表(你必须先创建记录)。

简而言之,该函数易于编写,您需要一个子对象(带有字段),以及一个父对象,它将容纳子对象类型为TABLE的子对象,并打开原始基础如果您在一个包中并且从该循环中读取该游标到单个记录中,则-table将SQL提取到SYS_REFCURSOR(您可能需要别名)。 该函数返回一种PARENT_OBJECT类型,但在其中包含带有光标值的记录子对象。

我希望这对你有用(如果你想创建OBJECT和Table函数,你的DBA可能会有许可问题)* /

答案 4 :(得分:0)

如果使用值操作,则可以编写函数。 在这里您可以找到有关如何操作的信息。它基本上就像用任何语言编写函数一样。您可以定义参数和返回值。 这使您很有可能只编写一次代码。这是你如何做到的:

http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_5009.htm