【问题标题】:How to reuse a large query without repeating it?如何在不重复的情况下重用大型查询?
【发布时间】:2012-02-25 12:41:19
【问题描述】:

如果我有两个查询,我将调用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),那也没关系。

【问题讨论】:

    标签: sql plsql dry


    【解决方案1】:
    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 独有的项目与处理丑查询 2 独有的项目不同。是否可以通过此查询判断哪些项目是唯一的?编辑:视图可能是纯 SQL 的最佳解决方案。
    • 当然,将您的两个集合操作包装在外部查询中,选择包含称为 Source 的固定列,即 SELECT 'horrible_query_1' AS Source, *SELECT 'ugly_query_2' AS Source, *。这样,您的 UNION 将为您提供查询中的所有列以及源标识符。
    • 最后一个例子,自从我最初提出这个问题以来,多年来使用“with 子句”一直很有帮助。这些“with 子句”有时称为公用表表达式(或 CTE)。这些是在搜索更多详细信息时需要了解的一些有用术语。 with 子句在 1999 年被添加到 SQL 标准中,因此大多数数据库都应该支持它们,除了 MySQL,它还没有实现这部分已有 20 年历史的标准。
    • 我想创建一个临时表或视图来执行搜索,但我不能这样做,因为我没有适当的数据库权限。相反,我使用了 WITH 子句,并且由于它,我可以编写单个查询而无需重复我自己的“horrible_query”。
    【解决方案2】:

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

    如果你想重用查询的结果,那么你应该考虑全局临时表。这些临时表存储会话或事务期间的数据(无论您选择哪个)。如果您需要多次重用计算数据,尤其是当您的查询确实“丑陋”和“可怕”(意味着长时间运行)时,这些非常有用。请参阅Temporary tables 了解更多信息。

    如果您需要将数据保存得比会话更长,您可以考虑物化视图

    【讨论】:

    【解决方案3】:

    由于您使用的是 Oracle,我将创建流水线 TABLE 函数。 该函数接受参数并返回一个对象(您必须创建它) 然后您使用 TABLE() 函数从中选择 * 甚至特定列,并且可以将其与 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 类型的子对象,然后打开原始基础- 如果您在一个包中,并且您从该游标从循环中读取到各个记录中,则将 SQL 提取到 SYS_REFCURSOR(您可能需要别名)中的表。 该函数返回一个 PARENT_OBJECT 类型,但在其内部将记录子对象与游标中的值打包在一起。

    我希望这对您有用(如果您想创建 OBJECT 和表函数,您的 DBA 可能存在权限问题)*/

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

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

        ALTER SESSION SET RESULT_CACHE_MODE=FORCE
        

        看看有没有帮助。

        【讨论】:

        • 这可能有助于提高性能,但我认为这个问题更多的是关于编码风格。
        猜你喜欢
        • 2014-06-06
        • 2022-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-12
        • 1970-01-01
        • 2012-03-28
        相关资源
        最近更新 更多