改进选择性能和可读性

时间:2015-07-17 20:53:29

标签: sql oracle performance readability

我使用的是oracle sql开发人员。 我有一个疑问,有一件事我有点麻烦: 1)我需要做一个有一些规则的选择。

  • 此选择必须从table1返回信息。
  • 表2是来自table1的“子”。
  • 每张桌子的日期必须与当前日期相同。
  • 两个表中的idrule一定不能为no。
  • 在最后一次返回时,它必须只有table1中的数据必须只是nrtable和nrgroupby在我所说的子选择中的数据。

如果不清楚我已经制作了一个,那可行,但它有点愚蠢,因为我认为有一些方法可以做到这一点,或者更有效或更具可读性,因为这种方式我不得不重写几乎同样选择两次:

    SELECT *
    FROM TABLE1
    WHERE NRSEQTABLE1 IN
      (SELECT DECODE(T1.NRSEQTABLE1,NULL, T2.NRSEQTABLE1, T1.NRSEQTABLE1) AS NRSEQTABLE1
      FROM
        (SELECT NRSEQTABLE1,
          NRNUM2
        FROM TABLE1 T1
        WHERE TO_CHAR(DTHRTABLE1, 'MM/YYYY') = TO_CHAR(SYSDATE, 'MM/YYYY')
        AND IDRULE                      = 'NO'
        ) T1
      FULL JOIN
        (SELECT NRSEQTABLE1,
          NRNUM2
        FROM TABLE2 T2
        WHERE TO_CHAR(DTHRTABLE2, 'MM/YYYY') = TO_CHAR(SYSDATE, 'MM/YYYY')
        AND IDRULE                       = 'NO'
        ) T2
      ON T2.NRSEQTABLE1 = T1.NRSEQTABLE1
      AND T2.NRNUM2       = T1.NRNUM2
      )
    AND NRGROUPBY IN
      (SELECT NRGROUPBY
      FROM TABLE1
      WHERE NRSEQTABLE1 IN
        (SELECT DECODE(T1.NRSEQTABLE1,NULL, T2.NRSEQTABLE1, T1.NRSEQTABLE1) AS NRSEQTABLE1
        FROM
          (SELECT NRSEQTABLE1,
            NRNUM2
          FROM TABLE1 T1
          WHERE TO_CHAR(DTHRTABLE1, 'MM/YYYY') = TO_CHAR(SYSDATE, 'MM/YYYY')
              AND IDRULE                      = 'NO'
          ) T1
        FULL JOIN
          (SELECT NRSEQTABLE1,
            NRNUM2
          FROM TABLE2 T2
          WHERE TO_CHAR(DTHRTABLE2, 'MM/YYYY') = TO_CHAR(SYSDATE, 'MM/YYYY')
          AND IDRULE                       = 'NO'
          ) T2
        ON T2.NRSEQTABLE1 = T1.NRSEQTABLE1
        AND T2.NRNUM2       = T1.NRNUM2
        )
      GROUP BY TABLE1.NRGROUPBY
      HAVING COUNT(TABLE1.NRSEQTABLE1) > 10
      )
    ORDER BY NRGROUPBY,
      NRSEQTABLE1;

对不起英语,感谢您的帮助

2 个答案:

答案 0 :(得分:3)

1 评估日期是否属于当月的一个好方法应该避免在列值上调用函数。你应该切换

TO_CHAR(DTHRTABLE1, 'MM/YYYY') = TO_CHAR(SYSDATE, 'MM/YYYY')

DTHRTABLE1 >= trunc(SYSDATE, 'MM') and DTHRTABLE1 < add_months(trunc(SYSDATE, 'MM'), 1)

允许优化器最终使用索引,并且每次查询执行只评估一次函数,而不是每个表行一次。

2 没有必要为您的目的执行任何完整加入。那些

where id in (
        select id
        from a
            full join b using (id...)
    )

是精神上的做法

where id in (
        select id 
        from a
    )
    or id in (
        select id 
        from b
    ) 

3 第二个子查询与查询的第一部分显着相同,因此您可以使用子查询因子来不对其进行两次计算。所以你可以切换

select id1, id2, xyx
from a
where (
        id1 in (
            select id1
            from a
        )
        or id1 in (
            select id1
            from b
        )
    )
    and id2 in (
        select id2
        from a
        where (
                id1 in (
                    select id1
                    from a
                )
                or id1 in (
                    select id1
                    from b
                )
            )
        group by id2
        having xyz2
    )

with src as (
        select id1, id2, xyx
        from a
        where (
                id1 in (
                    select id1
                    from a
                )
                or id1 in (
                    select id1
                    from b
                )
            )
    )
select *
from src
where id2 in (
        select id2
        from src
        group by id2
        having xyz
    )

5 但是,一旦通过联接的自我组变得如此明确,你可以将该模式切换到分析等效的女巫比它更快

select id1, id2, xyx
from (
        select id1, id2, xyx,
            COUNT(NRSEQTABLE1) over (partition by NRGROUPBY) as cnt
        from a
        where (
                id1 in (
                    select id1
                    from a
                )
                or id1 in (
                    select id1
                    from b
                )
            )
    )
where cnt > 10

如果您提供有关所涉及列的唯一性的详细信息(特别是NRSEQTABLE1,NRNUM2和NRGROUPBY),我可以建议您提高性能的一些可能更好的方法,但目前您可以尝试此查询:

select *
from (
        SELECT t.*,
            COUNT(NRSEQTABLE1) over (partition by NRGROUPBY) as cnt
        FROM TABLE1 t
        WHERE NRSEQTABLE1 IN (
                SELECT NRSEQTABLE1
                FROM TABLE1 T1
                WHERE DTHRTABLE1 >= trunc(SYSDATE, 'MM') and DTHRTABLE1 < add_months(trunc(SYSDATE, 'MM'), 1)
                    AND IDRULE = 'NO'
            ) 
            OR NRSEQTABLE1 IN (
                SELECT NRSEQTABLE1
                FROM TABLE2 T2
                WHERE DTHRTABLE2 >= trunc(SYSDATE, 'MM') and DTHRTABLE2 < add_months(trunc(SYSDATE, 'MM'), 1)
                    AND IDRULE = 'NO'
            )
    )
where cnt > 10

<强>更新

鉴于&#34; NRSEQTABLE1是来自table1的唯一Pk,但在table2中它是fk&#34;

第一个子查询可能没用,因为TABLE1的那些行

NRSEQTABLE1 IN (
        SELECT NRSEQTABLE1
        FROM TABLE1 T1
        WHERE DTHRTABLE1 >= trunc(SYSDATE, 'MM') and DTHRTABLE1 < add_months(trunc(SYSDATE, 'MM'), 1)
            AND IDRULE = 'NO'
    ) 

正是那些

的那些
DTHRTABLE1 >= trunc(SYSDATE, 'MM') and DTHRTABLE1 < add_months(trunc(SYSDATE, 'MM'), 1)
    AND IDRULE = 'NO'

然后你的查询将是

select *
from (
        SELECT t.*,
            COUNT(NRSEQTABLE1) over (partition by NRGROUPBY) as cnt
        FROM TABLE1 t
        WHERE (
                DTHRTABLE1 >= trunc(SYSDATE, 'MM') and DTHRTABLE1 < add_months(trunc(SYSDATE, 'MM'), 1)
                AND IDRULE = 'NO'
            )
            OR NRSEQTABLE1 IN (
                SELECT NRSEQTABLE1
                FROM TABLE2 T2
                WHERE DTHRTABLE2 >= trunc(SYSDATE, 'MM') and DTHRTABLE2 < add_months(trunc(SYSDATE, 'MM'), 1)
                    AND IDRULE = 'NO'
            )
    )
where cnt > 10

更仔细地阅读你的问题,我注意到你说&#34;两个表中的idrule一定不能没有#34; ,但是这个查询(因此也是原始的查询)不是&为此目的是正确的,因为它检查&#34; idrule是否等于&#39; NO&#39;在TABLE1和TABLE2 &#34;中的任何一个表中至少一次。

答案 1 :(得分:1)

您可以使用CTE上也知道的WITH条款。就是这个:

with t1 as 
     (SELECT NRSEQTABLE1, NRNUM2
     FROM TABLE1 T1
     WHERE TO_CHAR(DTHRTABLE1, 'MM/YYYY') = TO_CHAR(SYSDATE, 'MM/YYYY')
     AND IDRULE = 'NO'),
t2 as 
   (SELECT NRSEQTABLE1, NRNUM2
        FROM TABLE2 T2
        WHERE TO_CHAR(DTHRTABLE2, 'MM/YYYY') = TO_CHAR(SYSDATE, 'MM/YYYY')
        AND IDRULE = 'NO'),
t3 as 
    (select DECODE(T1.NRSEQTABLE1,NULL, T2.NRSEQTABLE1, T1.NRSEQTABLE1) AS NRSEQTABLE1
    from T1 FULL JOIN T2
      ON T2.NRSEQTABLE1 = T1.NRSEQTABLE1
      AND T2.NRNUM2       = T1.NRNUM2),
t4 as   
    (SELECT NRGROUPBY
      FROM TABLE1
      WHERE NRSEQTABLE1 IN
        (select NRSEQTABLE1 from t3) 
      GROUP BY TABLE1.NRGROUPBY
      HAVING COUNT(TABLE1.NRSEQTABLE1) > 10)
SELECT *
FROM TABLE1
WHERE NRSEQTABLE1 IN (select NRSEQTABLE1 from t3)            
    AND NRGROUPBY IN (select NRGROUPBY from t4)      
ORDER BY NRGROUPBY, NRSEQTABLE1;

通常它更容易阅读,并且很多时候它会提高性能,因为Oracle可以动态创建临时表来存储一些数据。

注意,上面的查询可能包含错误,我只是在没有任何测试的情况下快速完成。但是,您应该了解它的外观。