2012-04-20 82 views
6

我需要做的事情很奇怪,这是在一个视图创造有虚假记载填写产品价格的发布日期之间的差距之间的差距。其实,我的场景比这个稍微复杂一些,但我简化为产品/日期/价格。复制记录填写日期

比方说,我们有这个表:

create table PRICES_TEST 
(
    PRICE_DATE date   not null, 
    PRODUCT  varchar2(13) not null, 
    PRICE   number 
); 

alter table PRICES_TEST 
    add constraint PRICES_TEST_PK 
    primary key (PRICE_DATE, PRODUCT); 

有了这些记录:

insert into PRICES_TEST values (date'2012-04-15', 'Screw Driver', 13); 
insert into PRICES_TEST values (date'2012-04-18', 'Screw Driver', 15); 

insert into PRICES_TEST values (date'2012-04-13', 'Hammer', 10); 
insert into PRICES_TEST values (date'2012-04-16', 'Hammer', 15); 
insert into PRICES_TEST values (date'2012-04-19', 'Hammer', 17); 

选择记录将返回我:

PRICE_DATE    PRODUCT  PRICE     
------------------------- ------------- ---------------------- 
13-Apr-2012 00:00:00  Hammer  10      
16-Apr-2012 00:00:00  Hammer  15      
19-Apr-2012 00:00:00  Hammer  17      
15-Apr-2012 00:00:00  Screw Driver 13      
18-Apr-2012 00:00:00  Screw Driver 15      

今天假设是2012年4月21日,我需要查看,它应该每天重复每个价格联合国直到发布新的价格。像这样:

PRICE_DATE    PRODUCT  PRICE     
------------------------- ------------- ---------------------- 
13-Apr-2012 00:00:00  Hammer  10      
14-Apr-2012 00:00:00  Hammer  10      
15-Apr-2012 00:00:00  Hammer  10      
16-Apr-2012 00:00:00  Hammer  15      
17-Apr-2012 00:00:00  Hammer  15      
18-Apr-2012 00:00:00  Hammer  15      
19-Apr-2012 00:00:00  Hammer  17      
20-Apr-2012 00:00:00  Hammer  17      
21-Apr-2012 00:00:00  Hammer  17      
15-Apr-2012 00:00:00  Screw Driver 13      
16-Apr-2012 00:00:00  Screw Driver 13      
17-Apr-2012 00:00:00  Screw Driver 13      
18-Apr-2012 00:00:00  Screw Driver 15      
19-Apr-2012 00:00:00  Screw Driver 15      
20-Apr-2012 00:00:00  Screw Driver 15      
21-Apr-2012 00:00:00  Screw Driver 15      

任何想法如何做到这一点?我不能真的使用其他辅助表,触发器也没有PL/SQL编程,我真的需要这样做,使用一个视图

我认为这可以使用oracle分析来完成,但我并不熟悉这一点。我试图读这http://www.club-oracle.com/articles/analytic-functions-i-introduction-164/,但我没有得到它。

+0

NVM,我现在明白了吧:)这将有可能产生与创造性地使用'Dual'表的数据。 – mellamokb 2012-04-20 23:46:24

+0

我可以假设oracle分析具有它自己的日期维度表来执行此功能。你可以创建自己的日期维度表吗? – 2012-04-20 23:53:01

+0

这里是创造TSQL动态calandar视图一篇有趣的文章(是的,你要预言,但也许它可以改变):http://sqlserverpedia.com/blog/sql-server-bloggers/tsql-tuesday-18-使用-A-递归CTE到创建-A-日历表/ – 2012-04-20 23:58:00

回答

4

我想我已经用渐进的方式向与CTE的最终结果的解决方案:

with mindate as 
(
    select min(price_date) as mindate from PRICES_TEST 
) 
,dates as 
(
    select mindate.mindate + row_number() over (order by 1) - 1 as thedate from mindate, 
    dual d connect by level <= floor(SYSDATE - mindate.mindate) + 1 
) 
,productdates as 
(
    select p.product, d.thedate 
    from (select distinct product from PRICES_TEST) p, dates d 
) 
,ranges as 
(
    select 
    pd.product, 
    pd.thedate, 
    (select max(PRICE_DATE) from PRICES_TEST p2 
    where p2.product = pd.product and p2.PRICE_DATE <= pd.thedate) as mindate 
    from productdates pd 
) 
select 
    r.thedate, 
    r.product, 
    p.price 
from ranges r 
inner join PRICES_TEST p on r.mindate = p.price_date and r.product = p.product 
order by r.product, r.thedate 
  • mindate检索的数据尽早设置
  • dates生成日期从日历尽可能早到今天。
  • productdates交叉连接所有可能的产品与所有可能的日期
  • ranges确定哪些公司在每个日期
  • 其价格日期应用到实际价格的最终查询链接价格日期和过滤掉其中有没有相关的日期通过inner join条件价格日期

演示:http://www.sqlfiddle.com/#!4/e528f/126

+0

我只是删除了参照表双重和地板在所述第二“与”舍入选择。他们并不是真的需要。除此之外,它看起来很棒!我的真实场景在PK(包括产品,品牌,模型等)和其他几个信息栏(如价格,净重,笔记等)中包含2个以上的列,它仍然适用于适当的更改。非常感谢你。 – 2012-04-23 20:29:01

6

您可以使用该行发生器声明0语法,与表格中的不同产品进行交叉连接,然后将其外部连接到价格表。最后的接触是使用LAST_VALUE功能和IGNORE NULLS重复的价格,直到遇到一个新的价值,因为你想要一个观点,一个CREATE VIEW声明:

create view dense_prices_test as 
select 
    dp.price_date 
    , dp.product 
    , last_value(pt.price ignore nulls) over (order by dp.product, dp.price_date) price 
from (
     -- Cross join with the distinct product set in prices_test 
     select d.price_date, p.product 
     from (
      -- Row generator to list all dates from first date in prices_test to today 
      with dates as (select min(price_date) beg_date, sysdate end_date from prices_test) 
      select dates.beg_date + level - 1 price_date 
      from dual 
      cross join dates 
      connect by level <= dates.end_date - dates.beg_date + 1 
      ) d 
     cross join (select distinct product from prices_test) p 
    ) dp 
left outer join prices_test pt on pt.price_date = dp.price_date and pt.product = dp.product; 
+0

男人,你是一个高手!我从来没有听说过在分析功能中与'连接','连接'和'忽略空值'。非常感谢你。它与我更复杂的场景完美配合。 – 2012-04-21 03:47:06

+0

@LeoHolanda优秀!祝你好运。 – Wolf 2012-04-21 04:11:40

+0

嗨@Wolf,虽然你的答案是让我学习比别人多了一个,mellamokb的回答是,实际上产生了预期的效果,因此,他应得的“接受的答案”的标志之一。非常感谢你们帮助我的所有人。 – 2012-04-23 18:18:52

4

我做狼的出色答卷一些变化。

我用connect by中的常规子查询替换了子查询因子(WITH)。这使代码更简单一些。(虽然这种类型的代码看起来奇怪起初无论哪种方式,因此有可能不会在这里了巨大的收益。)

最显著,我用了一个分区外连接,而不是一个交叉连接和外部连接。分区外连接也有点奇怪,但它们恰好适用于这种类型的情况。这使得代码更简单,并且应该可以提高性能。

select 
    price_dates.price_date 
    ,product 
    ,last_value(price ignore nulls) over (order by product, price_dates.price_date) price 
from 
(
    select trunc(sysdate) - level + 1 price_date 
    from dual 
    connect by level <= trunc(sysdate) - 
     (select min(trunc(price_date)) from prices_test) + 1 
) price_dates 
left outer join prices_test 
    partition by (prices_test.product) 
    on price_dates.price_date = prices_test.price_date; 
+0

这是一个很好的改进。 WITH子句是来自其他一些SQL的工件,但我一直忘记分区外连接。工作很好。 – Wolf 2012-04-21 16:28:43

1

我刚刚意识到@Wolf和@jonearles改进不回我所需要的确切的结果,因为该行发生器列出所有日期将不会产生因产品的范围。如果产品A的第一个价格晚于产品B的任何价格,则产品A的第一个上市日期仍然必须相同。但他们真的帮助我进一步的工作,并取得了预期的效果:

我开始从这个不断变化的@狼的日期范围选择:

select min(price_date) beg_date, sysdate end_date from prices_test 

这样:

select min(PRICE_DATE) START_DATE, sysdate as END_DATE, PRODUCT 
from PRICES_TEST group by sysdate, PRODUCT 

但是,不知何故,每个产品的行数对于每个级别反复呈指数级增长。我只是在outter查询中添加了一个独特的。最终选择是这样的:

select 
    DP.PRICE_DATE, 
    DP.PRODUCT, 
    LAST_VALUE(PT.PRICE ignore nulls) over (order by DP.PRODUCT, DP.PRICE_DATE) PRICE 
from (
    select distinct START_DATE + DAYS as PRICE_DATE, PRODUCT 
    from 
    (
    -- Row generator to list all dates from first date of each product to today 
    with DATES as (select min(PRICE_DATE) START_DATE, sysdate as END_DATE, PRODUCT from PRICES_TEST group by sysdate, PRODUCT) 
    select START_DATE, level - 1 as DAYS, PRODUCT 
    from DATES 
    connect by level < END_DATE - START_DATE + 1 
    order by 3, 2 
) d order by 2, 1 
) DP 
left outer join prices_test pt on pt.price_date = dp.price_date and pt.product = dp.product; 

@Mellamokb解决方案实际上是我真正需要的,是肯定比我noobie更好的解决方案。

感谢的每个人不仅对我的帮助与此也为我呈现为“与”和“连接”等功能。