Oracle select 记录基于最小日期 where min date between

Oracle select records based on a min date where min date between

我的 SQL 目前是这样的。

SELECT t1.field1,
       t1.field2,
       t1.field3,
       t1.field4,
       t1.field5,
       t1.field6,
       t1.field7,
       t1.field8,
       t2.field1,
       t2.field2,
       t2.field3,
       t2.field4,
       t2.field5,
       t2.field6,
       t2.field7,
       t2.field8,
       t2.field9,
       t3.field1,
       t4.field1,
       t5.field1,
       SUM(t6.field1),
       MIN(t6.THEDATE) 

  FROM table1 t1
    LEFT JOIN table2 t2
      ON t1.field2 = t2.sameFieldName
      LEFT JOIN table3 t3
        ON t2.field9 = t3.sameFieldName
        LEFT JOIN table4 t4
          ON t1.field2 = t4.sameFieldName
          AND t2.field1 = t4.sameFieldName
          LEFT JOIN table5 t5
            ON t4.field1 = t5.sameFieldName
            LEFT JOIN table6 t6
              ON t4.field1 = t6.sameFieldName
              AND t4.colName1 = t6.sameFieldName

  WHERE t6.THEDATE BETWEEN SYSDATE - 70 AND SYSDATE - 50
    AND t1.field2 = 'SUBMIT'
    AND t1.field3 LIKE 'H%'

  GROUP BY t1.field1,
           t1.field2,
           t1.field3,
           t1.field4,
           t1.field5,
           t1.field6,
           t1.field7,
           t1.field8,
           t2.field1,
           t2.field2,
           t2.field3,
           t2.field4,
           t2.field5,
           t2.field6,
           t2.field7,
           t2.field8,
           t2.field9,
           t3.field1,
           t4.field1,
           t5.field1;

我遇到的问题是我需要 select 以最短日期为基础的条件。但是这样做会 "show" 最小日期,但它会根据最后一个 "theDate" 值过滤记录。我知道您不能在 where 子句中使用 agg 函数,因为 where 仅对单个记录进行操作。那么我怎样才能得到这样的东西呢?

SELECT *, sum(somthing), min(theDate)
FROM Table
WHERE min(theDate) BETWEEN SYSDATE - 70 AND SYSDATE - 50
GROUP BY <<<ALL GROUP COLUMNS>>>

您发布的语法无效 -- 您不能 group by * 并且您需要在 select * 上使用别名,因为您正在选择其他列。假设这两个只是将简化示例组合在一起的产物,您只需要使用 having 子句

SELECT a.*, sum(something), min(theDate)
  FROM table_name a
 GROUP BY <<list of columns in a>>
HAVING min(theDate) BETWEEN sysdate - 70 AND sysdate - 50

您可以使用解析函数:

SELECT * FROM
(
SELECT *, sum(somthing) over(), row_number() over (order by theDate) as rn
FROM Table
WHERE theDate BETWEEN SYSDATE - 70 AND SYSDATE - 50
)
WHERE rn = 1;

您可以使用 rank() 而不是 row_number(),但是如果在超过 1 行中使用最小日期值,则可能 return 超过 1 行。

根据数据的大小,在进行聚合之前过滤记录可能是最快的。合适的过滤器是:

SELECT *, sum(somthing), min(theDate)
FROM Table t
WHERE NOT EXISTS (SELECT 1 FROM table t2 WHERE . . . AND t2.thedate < SYSDATE - 70) AND
      EXISTS (SELECT 1 FROM table t2 WHERE . . . AND t2.thedate <= sysdate - 50)
GROUP BY *

. . . 是基于使用 * 注释的组的相等条件。

主要的性能消耗可能是 GROUP BY 中不必要的列。如果您的 Table 指的是非规范化的 table:

,就会发生这种情况
EMP (EMP_ID*, DEPT_ID, DEPT_NAME, SAL, THEDATE)

或者如果 Table 指的是连接,例如

EMP(EMP_ID*, DEPT_ID, SAL, THEDATE)
DEPT(DEPT_ID*, DEPT_NAME)

"Table" == EMP JOIN DEPT USING (DEPT_ID)

无论哪种情况,查询:

SELECT DEPT_ID, DEPT_NAME, SUM(SAL), MIN(THEDATE)
FROM   EMP
GROUP BY DEPT_ID, DEPT_NAME
HAVING MIN(THEDATE) >= SYSDATE-70 AND MIN(THEDATE) < SYSDATE-50;

将经历对 DEPT_NAME 进行分组的所有开销,即使 DEPT_NAME 对于给定的 DEPT_ID 始终具有相同的值。换句话说,DEPT_ID是所选列的候选键。如果 SELECT *, SUM(whatever) 中的“*”有一个或多个候选键(通常是所有“_ID”列)确定所有其他列的唯一值,那么这样做会更有效率:

SELECT DEPT_ID, MAX(DEPT_NAME) DEPT_NAME, SUM(SAL), MIN(THEDATE)
FROM   EMP USING (DEPT_ID)
GROUP BY DEPT_ID
HAVING MIN(THEDATE) >= SYSDATE-70 AND MIN(THEDATE) < SYSDATE-50;

如果您的依赖列是长字符串,性能差异可能会特别显着。

Gordon Linoff 的回答提出了一个很好的观点(尽管我认为他的实现可以进一步优化,见下文)——在某些情况下它对 "pre-filter" 有意义。很少有经验表明这可能会更快:
1) 大多数 (80% +) 行的 THEDATE 早于 70 天
2) Table 上的单个索引,其中包括 most/all GROUP BY 列,最好是 THEDATE
3) THEDATE 上的单独索引,或者 THEDATE 是 #2
中记录的索引的第一列 4) 或者对于 #3 - TableTHEDATE 分区(#2 中的索引作为本地索引会更好)

基本"pre-filter logic":对所有Table行求和
1) 不属于具有 "too old"
任何行的分组 2) 确实属于至少有 1 行的分组 "old enough but not too old"
3) 该行本身不是 "too old"

SELECT  DEPT_ID, DEPT_NAME, SUM(SAL), MIN(THEDATE)
FROM    EMP E1
WHERE   NOT EXISTS 
            (SELECT 1 FROM EMP E2 
             WHERE E2.DEPT_ID = E1.DEPT_ID 
                   AND E2.DEPT_NAME=E1.DEPT_NAME
                   AND E2.THEDATE < SYSDATE - 70)
        AND EXISTS
            (SELECT 1 FROM EMP E2 
             WHERE E2.DEPT_ID = E1.DEPT_ID 
                   AND E2.DEPT_NAME=E1.DEPT_NAME
                   AND E2.THEDATE BETWEEN SYSDATE-70 AND SYSDATE - 50)
       AND E1.THEDATE >= SYSDATE -70
GROUP BY DEPT_ID, DEPT_NAME;

最后说明:如果候选键分组和预过滤器优化似乎都适用,则可以串联应用它们:

SELECT  DEPT_ID, MAX(DEPT_NAME) DEPT_NAME, SUM(SAL), MIN(THEDATE)
FROM    EMP E1
WHERE   NOT EXISTS 
            (SELECT 1 FROM EMP E2 
             WHERE E2.DEPT_ID = E1.DEPT_ID 
                   AND E2.THEDATE < SYSDATE - 70)
        AND EXISTS
            (SELECT 1 FROM EMP E2 
             WHERE E2.DEPT_ID = E1.DEPT_ID 
                   AND E2.THEDATE BETWEEN SYSDATE-70 AND SYSDATE - 50)
       AND E1.THEDATE >= SYSDATE -70
GROUP BY DEPT_ID;

除此之外,您可能无法做更多的事情来提高查询的性能(尽管 PARALLELISM 可能是一个选项)。为了更快地获得正确的结果,您必须查看结构更改(mat 视图、索引、分区选项等)以支持查询。