2012-04-20 85 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更好的解決方案。

感謝的每個人不僅對我的幫助與此也爲我呈現爲「與」和「連接」等功能。