8

私は本当に奇妙なことをする必要があります。それは、製品価格の投稿日のギャップを埋めるために偽のレコードを作成することです。

実際、私のシナリオはそれよりも少し複雑ですが、製品/日付/価格に簡略化しています。

このテーブルがあるとしましょう:

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プログラミングを実際に使用することはできません。ビューを使用して、これを実行する必要があります

これはオラクル分析を使用して実行できると思いますが、私はそれに精通していません。このhttp://www.club-oracle.com/articles/analytic-functions-i-introduction-164/を読み込もうとしましたが、まったくわかりませんでした。

4

4 に答える 4

6

構文を使用して行ジェネレーター ステートメントを作成しCONNECT BY LEVEL、テーブル内の個別の製品とクロス結合してから、それを価格テーブルに外部結合できます。最後の仕上げは、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;
于 2012-04-21T00:23:46.647 に答える
5

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

于 2012-04-21T00:23:35.133 に答える
4

ウルフの優れた答えにいくつかの変更を加えました。

のサブクエリのファクタリング ( WITH) を通常のサブクエリに置き換えましたconnect by。これにより、コードが少し単純になります。(ただし、このタイプのコードはどちらにしても最初は奇妙に見えるため、ここで大きな利点はないかもしれません。)

最も重要なのは、クロス結合と外部結合の代わりにパーティション外部結合を使用したことです。パーティションの外部結合もちょっと変わっていますが、まさにこのタイプの状況を対象としています。これにより、コードが単純になり、パフォーマンスが向上するはずです。

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;
于 2012-04-21T04:30:34.167 に答える
1

@Wolf と @jonearles の改善は、すべての日付をリストする行ジェネレーターが製品ごとの範囲を生成しないため、必要な正確な結果を返さないことに気付きました。製品 A の最初の価格が製品 B のどの価格よりも遅い場合でも、製品 A の最初にリストされた日付は同じでなければなりません。しかし、彼らは私がさらに作業を進め、期待される結果を得るのを本当に助けてくれました:

@wolf の日付範囲セレクターをこれから変更することから始めました。

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

しかし、どういうわけか、製品ごとの行数はレベルごとに指数関数的に増加しています。外部クエリに個別を追加しました。最終的な選択はこれでした:

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ソリューションは、実際に私が本当に必要としているものであり、確かに初心者のソリューションよりも優れています。

これを手伝ってくれただけでなく、「with」や「connect by」などの機能を紹介してくれた皆さんに感謝します。

于 2012-04-23T17:59:26.110 に答える