0

我有来自两个表的一系列产品的大量数据,我想根据当前日期在指定的日期范围内显示这些数据,并按功能分组。数据范围为:

假设今天的日期是 03/09/2012 (DD/MM/YYYY)

--Product 1--
Everything 5 years ago 'Before 2007'  {field1}   {field2}   {field3} 
4 years ago          '2008' 
3 years ago          '2009' 
2 years ago          '2010' 
1 year by month      'Jan 2011' 
'Feb 2011' 
'Mar 2011 
.... 
.... 
'Dec 2011' 
Sum of 1 year ago    '2011' 
This year by month   'Jan 2012' 
'Feb 2012' 
'Mar 2012' 
.... 
....
'Sept 2012' 
Sum of this year '2012'

这个sql的性能很重要。到目前为止,我得到了一个 sql,它可以按年或月对每个产品进行进一步分组,但不是按上述顺序。我正在考虑使用 NVL、CASE 和许多嵌套的 sql,但任何人都可以想出一个可以获得良好性能的解决方案吗?

    SELECT EXTRACT (YEAR FROM {DATE}) "YEAR", EXTRACT (MONTH FROM {DATE}) "MONTH", SUM({field1}) as A, SUM({field 2}) as B ,COUNT(1) as {field 3}
    FROM (
            SELECT {Field A}, DECODE({Field Key1}, NULL, 0, 1) {field 1}, DECODE({field B}, NULL, 1, 0)  {field2}, {Field Key2}
            FROM {table A}, (
                    SELECT {field key2}
                    FROM {table B}
                    WHERE {conditions} B  
            WHERE A.KEY= B.KEY(+)
    )
    where {conditions}
    GROUP BY EXTRACT (YEAR FROM {DATE}) , EXTRACT (MONTH FROM {DATE}) 
  ) DATASET   
4

2 回答 2

1

我有点不清楚你在努力解决哪个部分,或者为什么你提到许多嵌套的 SQL。没有任何样本数据,我不得不编造一些:

create table t42 (my_date date, my_value number);

insert into t42 values (date '2006-01-31', 0601);
insert into t42 values (date '2006-12-31', 0612);
insert into t42 values (date '2007-01-31', 0701);
insert into t42 values (date '2007-12-31', 0712);
insert into t42 values (date '2008-01-31', 0801);
insert into t42 values (date '2008-12-31', 0812);
insert into t42 values (date '2009-01-31', 0901);
insert into t42 values (date '2009-12-31', 0912);
insert into t42 values (date '2010-01-31', 1001);
insert into t42 values (date '2010-12-31', 1012);
insert into t42 values (date '2011-01-31', 1101);
insert into t42 values (date '2011-12-31', 1112);
insert into t42 values (date '2012-01-31', 1201);
insert into t42 values (date '2012-02-29', 1202);
insert into t42 values (date '2012-03-31', 1203);
insert into t42 values (date '2012-04-30', 1204);
insert into t42 values (date '2012-05-31', 1205);

然后,您可以使用内部查询为期间/年/月生成“标签”,并使用虚拟字段对结果进行排序,加上您实际感兴趣的值。然后使用外部查询来执行任何操作sumcount等等.

select label, sum(my_value), count(1)
from
(
    select
        case when my_date < trunc(sysdate, 'YYYY') - interval '4' year then
                'Before ' || to_char(trunc(sysdate, 'YYYY')
                - interval '5' year, 'YYYY')
            when my_date < trunc(sysdate, 'YYYY') - interval '1' year then
                to_char(my_date, 'YYYY')
            else to_char(my_date, 'Mon YYYY')
        end as label,
        case when my_date < trunc(sysdate, 'YYYY') - interval '4' year then
                to_char(trunc(sysdate, 'YYYY') - interval '5' year, 'YYYYMM')
            when my_date < trunc(sysdate, 'YYYY') - interval '1' year then
                to_char(my_date, 'YYYY') || '01'
            else to_char(my_date, 'YYYYMM')
        end as order_field,
        my_value
    from t42
)
group by label, order_field
order by order_field;

trunc(sysdate, 'YYYY')用来查找当前年份的开始,然后interval用于返回五年,并根据这些“桶”生成labelorder_field伪列。像这样使用case让我有不同的桶——一个用于超过五年的所有东西,一个用于直到去年的每一年,一个用于从那以后的每个月。

LABEL             SUM(MY_VALUE)   COUNT(1)
----------------- ------------- ----------
Before 2007                2626          4
2008                       1613          2
2009                       1813          2
2010                       2013          2
Jan 2011                   1101          1
Dec 2011                   1112          1
Jan 2012                   1201          1
Feb 2012                   1202          1
Mar 2012                   1203          1
Apr 2012                   1204          1
May 2012                   1205          1

我只打了一次表,所以性能应该取决于你如何提取原始数据(你的连接和条件),而不是你如何操作它。显然,您可以用t42两个表之间的当前连接替换,并提取您感兴趣的字段。

我建议您切换到 ANSI 连接语法,而不是 Oracle(+)用于外部连接的旧表示法。这不涉及任何没有任何数据的年份或月份,但您的原始大纲也没有,所以这可能不是问题。您如何产生或至少显示“年度总和”值可能取决于您的客户。


将标签生成分离到一个视图中以使其可重用并允许您查找没有数据的时段:

create or replace view v42 (period_label, period_order, period_start, period_end)
as
select 'Before ' || to_char(trunc(sysdate, 'YYYY') - interval '5' year, 'YYYY'),
    '197001',
    date '1970-01-01',
    trunc(sysdate, 'YYYY') - interval '4' year  - interval '1' second
from dual
union
select to_char(year_start, 'YYYY'),
    to_char(year_start, 'YYYY') || '01',
    year_start,
    year_start + interval '1' year - interval '1' second
from (
    select add_months(trunc(sysdate, 'YYYY'), - 12 * (level + 1)) as year_start
    from dual connect by level <= 3
)
union
select to_char(month_start, 'Mon YYYY'),
    to_char(month_start, 'YYYYMM'),
    month_start,
    month_start + interval '1' month - interval '1' second
from (
    select add_months(trunc(sysdate, 'MM'), 1 - level) as month_start
    from dual connect by level <= 12 + to_number(to_char(sysdate, 'MM'))
);

这是为了生产您最初拥有的标签;如果您希望单独显示所有年份,请删除 的第一部分union,您可以调整connect by条款以改变按月显示的年份。(您可以对其进行参数化,但这可能有点远)。

你有三类“桶”,一类是按月分类,一类是按年分类,然后是对过去太远的东西的统称;联合的每个部分都针对其中一个部分,为每个类中的所有存储桶生成周期开始和结束日期,以及标签和稍后订购的东西。查看视图,也许每个select单独的视图,看看他们在做什么。

然后将该视图加入您的数据表或表;如果要显示没有匹配数据的标签,请使用左外连接:

select v.period_label, nvl(sum(t.my_value), 0), count(t.my_value)
from v42 v
left join t42 t on t.my_date between v.period_start and v.period_end
group by v.period_label, v.period_order
order by v.period_order;

PERIOD_LABEL      NVL(SUM(T.MY_VALUE),0) COUNT(T.MY_VALUE)
----------------- ---------------------- -----------------
Before 2007                         2626                 4
2008                                1613                 2
2009                                1813                 2
2010                                2013                 2
Jan 2011                            1101                 1
Feb 2011                               0                 0
...
Nov 2011                               0                 0
Dec 2011                            1112                 1
...
于 2012-09-03T10:31:54.020 回答
0
select * from (
  select t.*
         ,case 
            when extract(YEAR FROM t.date) < 2007
              then 0
            else
              extract(YEAR FROM t.date)
          end as nYear
         ,case
            when extract(YEAR FROM t.date) < 2007
              then 0
            else
              extract(MONTH FROM t.date)
          end as nMonth       
  from table_name t
)d
group by d.nYear, d.nMonth
于 2012-09-03T05:54:34.637 回答