【问题标题】:How to optimize query in Oracle with max in where clause如何在where子句中使用max优化Oracle中的查询
【发布时间】:2015-09-03 13:30:40
【问题描述】:

我提供假设的表格和查询来解释我的问题。请原谅我的任何语法错误。

部门表:

ID Dno Dname BDate   seq
1  1     A    5-Aug   0
2  1     B    3-Aug   0
3  1     B    7-Aug   1
4  1     C    2-Aug   0

以下查询返回上表中的第 1 条和第 3 条记录:

select * from Dept where BDate > (select mydate from other_table)
-- mydate is 4-Aug

然后我在查询中进行了以下更改以返回第二条记录。因为对于 Dname 'B',我们有一条 Bdate > 4-Aug 的记录。

select * from Dept D where
(SELECT MAX(BDATE)
FROM Dept D1
WHERE D1.Dno = D.Dno
AND D1.Dname = D.Dname
) > (select mydate from other_table)

上面的查询有效,但它影响了性能。我该如何优化它。

我想到了联合或移动最大查询来选择部分。但是,找不到方法。

【问题讨论】:

    标签: oracle query-optimization


    【解决方案1】:

    假设我正确理解了您的逻辑(如果最大日期大于指定日期,您希望给定 dno 和 dname 的所有行)并且检索“mydate”的查询返回单行,我会这样做类似:

    with     dept as (select 1 id, 1 dno, 'A' dname, to_date('05/08/2015', 'dd/mm/yyyy') bdate, 0 seq from dual union all
                      select 2 id, 1 dno, 'B' dname, to_date('03/08/2015', 'dd/mm/yyyy') bdate, 0 seq from dual union all
                      select 3 id, 1 dno, 'B' dname, to_date('07/08/2015', 'dd/mm/yyyy') bdate, 0 seq from dual union all
                      select 4 id, 1 dno, 'C' dname, to_date('02/08/2015', 'dd/mm/yyyy') bdate, 0 seq from dual),
      other_table as (select to_date('04/08/2015', 'dd/mm/yyyy') mydate from dual)
    select id,
           dno,
           dname,
           bdate,
           seq
    from   (select d.*,
                   max(bdate) over (partition by dno, dname) max_bdate
            from   dept d)
    where  max_bdate > (select mydate from other_table);
    
            ID        DNO DNAME BDATE             SEQ
    ---------- ---------- ----- ---------- ----------
             1          1 A     05/08/2015          0
             2          1 B     03/08/2015          0
             3          1 B     07/08/2015          0
    

    【讨论】:

    • 它可以工作,但需要更长的时间。和解释计划成本是 373037。解释计划成本是 86831 与问题中提到的查询。
    • 我不会太在意比较不同查询的成本。如果这个查询的性能不如另一个,那就不要使用它! *{:-)
    【解决方案2】:

    如果您希望此查询返回DEPT 表中的一小部分行,这对您来说可能更快。我假设DEPT.DNAME 是唯一的,并且上面有一个索引。 (当然,DEPT.BDATE 上需要有一个索引!)

    with     dept as (select 1 id, 1 dno, 'A' dname, to_date('05/08/2015', 'dd/mm/yyyy') bdate, 0 seq from dual union all
                      select 2 id, 1 dno, 'B' dname, to_date('03/08/2015', 'dd/mm/yyyy') bdate, 0 seq from dual union all
                      select 3 id, 1 dno, 'B' dname, to_date('07/08/2015', 'dd/mm/yyyy') bdate, 0 seq from dual union all
                      select 4 id, 1 dno, 'C' dname, to_date('02/08/2015', 'dd/mm/yyyy') bdate, 0 seq from dual),
      other_table as (select to_date('04/08/2015', 'dd/mm/yyyy') mydate from dual)
    select id,
           dno,
           dname,
           bdate,
           seq
    from   dept WHERE dname IN ( SELECT d2.dname FROM dept d2 WHERE d2.bdate >  (select mydate from other_table) );
    

    【讨论】:

      【解决方案3】:

      以下查询运行速度快 4 倍,结果正确:

      select d1.* from Dept d1,
      (select dno, dname, MAX(BDATE) as maxdate from Dept group by dno, dname) d2
      where
      d1.dno=d2.dno and d1.dname=d2.dname
      and d2.maxdate > (select mydate from other_table)
      

      【讨论】:

        【解决方案4】:

        使用 group-by 子句避免子查询之一:

        select Dno, Dname, max(BDate)
        from Dept
        group by Dno, Dname
        having Max(BDate) > (select mydate from other_table)
        

        您还可以使用本地 var 删除其他子查询:

        declare @mydate Date = (select mydate from other_table);
        select Dno, Dname, max(BDate)
        from Dept
        group by Dno, Dname
        having Max(BDate) > (@mydate)
        

        【讨论】:

        • 第二个查询对于 Oracle 来说是无效的 SQL。
        • @MTO - 谢谢,我不是 Oracle 人员,但这就是我在 MS-SQL 中的做法。
        • 在实际场景中,我有很多列,所以我认为将 50 列左右分组不会提高性能。
        • @Vikas - 哇... 50 列。首先,您不需要使用所有可用的列,只需使用相关列进行选择和分组。或者第二,考虑重构您的架构以将员工数据分解到一个单独的表中,将其从您的部门表中删除。 (我在这里做了一些假设)。
        • 以上查询不能作为最大分组功能在 where 子句中使用。将其移动到有子句时,查询仅返回第 1 条和第 3 条记录,而不是第 2 条记录。
        【解决方案5】:

        根据数据集的选择性,可能值得尝试这种方法:

        select *
        from   dept
        where  (dno, dname) in (
                 select distinct dno, dname
                 from   dept
                 where  BDATE > (select mydate from other_table))
        

        使用 dept(bdate) 和 dept(dno,dname) 上的索引,并且只有很少的记录要匹配,这将非常快。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-02-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-14
          相关资源
          最近更新 更多