6

我需要帮助编写关于 Oracle 的老化报告。报告应该是这样的:

 aging file to submit total       17
 aging file to submit 0-2 days    3
 aging file to submit 2-4 days    4
 aging file to submit 4-6 days    4
 aging file to submit 6-8 days    2 
 aging file to submit 8-10 days   4

我可以为每个部分创建一个查询,然后合并所有结果,例如:

select 'aging file to submit total  ' || count(*) from FILES_TO_SUBMIT where trunc(DUE_DATE) > trunc(sysdate) -10
union all
select 'aging file to submit 0-2 days ' || count(*) from FILES_TO_SUBMIT where trunc(DUE_DATE) <= trunc(sysdate)  and trunc(DUE_DATE) >= trunc(sysdate-2)
union all
select 'aging file to submit 2-4 days ' || count(*) from FILES_TO_SUBMIT where trunc(DUE_DATE) <= trunc(sysdate-2) and trunc(DUE_DATE) >= trunc(sysdate-4) ;

我想知道是否有更好的方法使用 oracle 分析函数或任何其他可以获得更好性能的查询?

样本数据:

CREATE TABLE files_to_submit(file_id int,   file_name varchar(255),due_date date); 

INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 1, 'file_' || 1, sysdate);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 2, 'file_' || 2, sysdate -5);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 3, 'file_' || 3, sysdate -4);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 4, 'file_' || 4, sysdate);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 5, 'file_' || 5, sysdate-3);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 6, 'file_' || 6, sysdate-7);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 7, 'file_' || 7, sysdate-10);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 8, 'file_' || 8, sysdate-12);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 9, 'file_' || 9, sysdate-3);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 10, 'file_' || 10, sysdate-5);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 11, 'file_' || 11, sysdate-6);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 12, 'file_' || 12, sysdate-7);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 13, 'file_' || 13, sysdate-5);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 14, 'file_' || 14, sysdate-4);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 15, 'file_' || 15, sysdate-2);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 16, 'file_' || 16, sysdate-6);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 17, 'file_' || 17, sysdate-6);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 18, 'file_' || 18, sysdate-5);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 19, 'file_' || 19, sysdate-10);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES  ( 20, 'file_' || 20, sysdate-9);


DROP TABLE files_to_submit;
4

5 回答 5

5

您可以使用这种简单的方法来获取所有天的报告(不包括总数):

select 
'aging file to submit '|| trunc(dist/2)*2 ||'-'|| (trunc(dist/2)*2+2) || ' days: ' ||  count(*)
from (
      select trunc(sysdate) - trunc(DUE_DATE) as dist
      from FILES_TO_SUBMIT 
      --where trunc(DUE_DATE) > trunc(sysdate) -10
)
group by trunc(dist/2)
order by trunc(dist/2);

唯一重要的是天数(dist(ance) 字段)。

如果您想在同一扫描中也有总计:

select 
'aging file to submit '|| 
 case 
    when trunc(dist/2) is null 
    then 'Total ' 
    else trunc(dist/2)*2 ||'-'|| (trunc(dist/2)*2+2) || ' days: ' 
 end  ||  
 count(*)
from (
      select trunc(sysdate) - trunc(DUE_DATE) as dist
      from FILES_TO_SUBMIT 
      where trunc(DUE_DATE) > trunc(sysdate) -10
)
group by rollup(trunc(dist/2))
order by trunc(dist/2)
nulls first;

提示:如果您有数百天的历史记录,索引会很有用。(注意:如果你的表很大,>100Milion,创建索引会需要一些时间)

create index index_name on files_to_submit(due_date);

然后将条件更改为:

where DUE_DATE > trunc(sysdate) - 10

这将加快 y

于 2015-03-03T07:57:28.313 回答
3

请允许我建议WIDTH_BUCKET。这会将日期范围分成相等的大小。由于您希望将 10 天分为 2 天一组,因此存储桶大小将为 10 / 2 = 5。

询问:

SELECT 
    CASE GROUPING(bucket) 
        WHEN 1 
            THEN 'aging file to submit Total' 
            ELSE 'aging file to submit ' || (bucket-1)*2 || '-' || (bucket)*2 || ' days'
    END             AS bucket_number, 
    COUNT(1)        AS files
FROM (
    SELECT 
        WIDTH_BUCKET(due_date, sysdate, sysdate-10, 5) bucket 
    FROM 
        files_to_submit
    WHERE 
        due_date >= sysdate-10
    )
GROUP BY
    ROLLUP(bucket)
ORDER BY
    bucket NULLS FIRST;

结果:

BUCKET_NUMBER                             FILES
------------------------------------ ----------
aging file to submit Total                   17
aging file to submit 0-2 days                 2
aging file to submit 2-4 days                 3
aging file to submit 4-6 days                 6
aging file to submit 6-8 days                 5
aging file to submit 8-10 days                1
于 2015-03-06T11:48:01.813 回答
2

我使用您的样本数据得到了不同的计数 - 我总共得到 19 个而不是 17 个(这似乎是合适的,因为您的样本数据中只有 20 条记录超出范围):

WITH d1 AS (
    SELECT 2 AS day_cnt FROM dual
     UNION ALL
    SELECT 4 FROM dual
     UNION ALL
    SELECT 6 FROM dual
     UNION ALL
    SELECT 8 FROM dual
     UNION ALL
    SELECT 10 FROM dual
)
SELECT NVL(title, 'aging file to submit total') AS title, COUNT(DISTINCT file_id)
  FROM (
    SELECT 'aging file to submit ' || prev_day || '-' || day_cnt || ' days' AS title, f1.file_id
      FROM (
        SELECT day_cnt, NVL(LAG(day_cnt) OVER ( ORDER BY day_cnt ), 0) AS prev_day
          FROM d1
    ) d2, files_to_submit f1
     WHERE TRUNC(f1.due_date) <= TRUNC(SYSDATE - d2.prev_day)
       AND TRUNC(f1.due_date) >= TRUNC(SYSDATE - d2.day_cnt)
) GROUP BY ROLLUP(title);

此外,日期范围的计数不正确(它们加起来不等于 19,即),因为由于使用TRUNC()并包括两种最终情况,文件可能会被计算两次。但我相信你可以调整上面的内容来提供你想要的。

于 2015-02-27T02:44:32.627 回答
1

这种方法允许您将存储桶与主 SQL 分开维护,以防您希望它们具有不同的大小或将它们命名为不是从 SQL 生成的名称,例如“准时”、“拖欠”等。提供了一个可读性很强的主 SQL 块。

with aging as
 (select count(*) count_per_day, (trunc(sysdate) - trunc(f.due_date)) age
    from files_to_submit f
    where trunc(f.due_date - 10) <= sysdate
   group by (trunc(sysdate) - trunc(f.due_date))),
buckets as
 (select 1 bucket_id, 0 bucket_min, 2 bucket_max, 'aging file to submit 0-2' bucket_name from dual
  union select 2, 2,  4, 'aging file to submit 2-4' from dual
  union select 3, 4,  6, 'aging file to submit 4-6' from dual
  union select 4, 6,  8, 'aging file to submit 6-8' from dual
  union select 5, 8, 10, 'aging file to submit 8-10' from dual
  union select 6, null, null, 'aging file to submit total' from dual
)
select nvl(b.bucket_name, (select bucket_name from buckets where bucket_id = 6)), sum(a.count_per_day) bucket_cnt
  from aging a
  join buckets b on (a.age >= b.bucket_min and a.age <= b.bucket_max)
 group by rollup(b.bucket_name)
 order by b.bucket_name nulls first;
于 2015-03-04T21:58:49.107 回答
1
WITH r (
    'aging file to submit ' Prefix,
    Total,
    Days0_2,
    Days2_4,
    Days4_6,
    Days6_8,
    Days8_10
    ) AS (
    SELECT 
        SUM(Total) Total,
        SUM(Days0_2) Days0_2,
        SUM(Days2_4) Days2_4,
        SUM(Days4_6) Days4_6,
        SUM(Days6_8) Days6_8,
        SUM(Days8_10) Days8_10
      FROM (
            SELECT 
                (CASE WHEN f.days <= 2 THEN f.num ELSE NULL END) AS Days0_2,
                (CASE WHEN f.days >= 2 AND f.days <= 4 THEN f.num ELSE NULL END) AS Days2_4,
                (CASE WHEN f.days >= 4 AND f.days <= 6 THEN f.num ELSE NULL END) Days4_6,
                (CASE WHEN f.days >= 6 AND f.days <= 8 THEN f.num ELSE NULL END) AS Days6_8,
                (CASE WHEN f.days >= 8 AND f.days <= 10 THEN f.num ELSE NULL END) AS Days8_10,
                f.num AS Total
            FROM (
                SELECT 
                    COUNT(*) AS num,
                    TRUNC(due_date) - TRUNC(SYSDATE) + 10 AS days
                FROM FILES_TO_SUBMIT t
                WHERE (TRUNC(due_date) - TRUNC(SYSDATE) + 10) >= 0
                GROUP BY TRUNC(due_date) - TRUNC(SYSDATE) + 10
            ) f
    ) s
) 
SELECT Prefix || 'Total' AS Label, Total AS Count FROM r
UNION ALL SELECT Prefix || '0-2 days',  Days0_2   FROM r
UNION ALL SELECT Prefix || '2-4 days',  Days2_4   FROM r
UNION ALL SELECT Prefix || '4-6 days',  Days4_6   FROM r
UNION ALL SELECT Prefix || '6-8 days',  Days6_8   FROM r
UNION ALL SELECT Prefix || '8-10 days', Days8_10  FROM r

它不会重复计算 Total 行的记录。由于您的日期范围重叠,您无法将各个计数相加得出总数。作为此处给出的另一个查询,总共有 25 条记录,只有 20 条记录和 1 条超出范围。

总计的结果是您对 20 条记录和 1 条记录 12 天的预期结果。最里面的查询完成了所有繁重的工作,但它执行一次以获得所有老化结果。其结果最多为 11 行,0-10 天。其余的查询用于最终结果和漂亮的输出。

您可以通过 SUMing 一个级别来消除一个查询级别,我只是发现通过能够选择中间查询进行抽查更容易验证结果。

这是查询的结果:

在此处输入图像描述

于 2015-03-06T10:52:08.093 回答