【问题标题】:Improve Select perfomance and readability提高 Select 性能和可读性
【发布时间】:2015-07-19 15:49:53
【问题描述】:

我使用oracle sql developer。 我有一个疑问和一件事,我遇到了一些麻烦: 1)我需要做一个有一些规则的选择。

  • 此选择必须返回表 1 中的信息。
  • Table2 是 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;

抱歉,英语不好,感谢您的帮助

【问题讨论】:

    标签: sql oracle performance readability


    【解决方案1】:

    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
    

    更新

    鉴于 “NRSEQTABLE1 是 table1 中的唯一 Pk,但在 table2 中它是 fk”

    第一个子查询可能没用,因为 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
    

    仔细阅读你的问题,我注意到你说“两个表中的 idrule 都不能是 no”,但是这个查询(因此也是原始查询)不适合这个目的是因为它会检查“idrule 在表 TABLE1 和 TABLE2 中的任何一个中是否至少一次等于 'NO'”。

    【讨论】:

    • 使用 union 代替它并不总是更好,但也可能更好。它主要取决于数据的基数、相关列的选择性以及它们上是否存在有用的索引。我还认为如果 NRSEQTABLE1 是唯一的,那么第一个子查询是无用的,但是缺少有关此的详细信息。
    • 我刚刚意识到,如果 OP 正在寻找“COUNT(NRSEQTABLE1)>10”,那么该列不是唯一的并且它会出现很多次。所以第一个子查询不是没用的,如果两个表的 NRSEQTABLE1 列上都没有索引,使用 UNION 可能会更好。
    • 真的很抱歉,我完全忘记描述有关列的信息,但没有你提供了很好的帮助。我想我从未见过这个分区和命令,但知道你已经向我解释过我会尝试了解它们。但只有一个问题,这个命令是oracle独有的,还是可以在任何数据库中使用?这里是信息:NRSEQTABLE1是table1中唯一的Pk,但在table2中它是fk,因此它在table2中出现了很多次,即这就是我使用完整连接的原因。 Nrgroupby 是来自 table1 上其他表的 fk,这在这里并不重要...
    • 而且也出现多次。 Nrnum2 也是来自其他表的 fk,并且它不仅通过 table1,而且通过 table2 多次重复其值。它们都是数值。无论如何,感谢您的帮助。抱歉,不仅仅是因为这个愚蠢的问题,我对 sql 很陌生(刚学了第二年,还在上大学(我知道这对学习 sql 没有任何意义)),还因为我没有'不要描述列。 (再次对不起英语)
    • 分析功能当然也可以在 PostgreSQL 上使用,并且也应该由许多其他 RDBMS 实现。我在 PK 规范之后更新了我的答案。但是我发现您的初始要求和工作查询(关于 IDRULE)之间存在歧义。我在答案的最后陈述中谈到了它。尝试纠正问题中的错误部分以使其清楚。
    【解决方案2】:

    您可以使用WITH 子句也知道在 CTE。就是这个:

    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 可能会即时创建一个临时表来存储一些数据。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-19
      • 2018-09-06
      • 1970-01-01
      • 2014-06-11
      • 1970-01-01
      • 2016-08-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多