4

週 X の製品在庫と最新の予測を選択する、週、製品、在庫、および週ごとの予測を含む一連のテーブルがあります。しかし、私はSQLを手に入れることができません:

create table products (
    product_id integer
);
create table inventory (
    product_id integer,
    asof_week integer,
    qoh float8
);
create table forecast (
    product_id integer,
    for_week integer,
    asof_week integer,
    projection float8
);
create table weeks (
    wkno integer
);
insert into weeks values (4),(5),(6),(7);
insert into products values(1),(2);
insert into inventory values(1,5,10),(1,6,20),(2,6,200);
insert into forecast values(1,4,1,10),(1,4,2,11),(1,4,3,12),(1,4,4,13),
                           (1,5,1,11),(1,5,2,11),(1,5,3,21),(1,5,4,31),
--corr:one too many        (1,6,1,10),(1,6,2,11),(1,6,3,12),(1,6,4,22),(1,6,5,32),(1,6,5,42),(1,6,6,42),
                           (1,6,1,10),(1,6,2,11),(1,6,3,12),(1,6,4,22),(1,6,5,42),(1,6,6,42),
                           (1,7,1,10),(1,7,6,16),
                           (2,6,5,2000),(2,7,5,2100),(2,8,5,30);

そしてクエリ:

select p.product_id "product",
        i.asof_week "inven asof",
        i.qoh "qoh",
        f.for_week "fcast for",
        f.projection "fcast qty",
        f.asof_week "fcast asof"
from weeks w, products p
    left join inventory i on(p.product_id = i.product_id)
    left join forecast f on(p.product_id = f.product_id)
where
    (i.asof_week is null or i.asof_week = w.wkno)
    and (f.for_week is null or f.for_week = w.wkno)
    and (f.asof_week is null
        or f.asof_week = (select max(f2.asof_week)
                          from forecast f2
                          where f2.product_id = f.product_id
                             and f2.for_week = f.for_week))
order by p.product_id, i.asof_week, f.for_week, f.asof_week

たとえば、4 ~ 7 週目では、結果セットを探しています。

product week    qoh     projection
1       4       -       13
1       5       10      31
1       6       20      42
1       7       -       16
2       6       200     2000
2       7       -       2100

しかし、実際には3行しか得られません:

 product | inven asof | qoh | fcast for | fcast qty | fcast asof 
---------+------------+-----+-----------+-----------+------------
       1 |          5 |  10 |         5 |        31 |          4
       1 |          6 |  20 |         6 |        42 |          6
       2 |          6 | 200 |         6 |      2000 |          5
(3 rows)
Time: 2.531 ms

私はSQLに慣れていないので、いくつかの便利なポインターを使用できます。

データに関するいくつかの注意事項: この問題に集中するために例から省略した他のいくつかのデータ テーブルを結合する必要があります。そのうちの少なくとも 1 つは、予測数量テーブルと性質が似ています (つまり、すべての製品に複数のバージョンの行があります)。 x 週間)。製品X週ごとに約100行の予測行があるため、どこかで効率についても心配する必要があります...しかし、最初に結果を修正してください。

私はpostgresql 9.2を使用しています。

ありがとう。

4

3 に答える 3

2

データ モデルの残りの部分を知らずに一般的な指針を示すことは困難ですが、これは言うまでもありません。一般に、クエリをできるだけ「フラット」に保つと、クエリを推論しやすくなります。また、null チェックがたくさんあるとすぐに、データに保証を追加するか、クエリを別の「ルート」テーブルに再度ピボットします。

とにかく、以下はあなたのために働くはずです(ただし、特に重複が存在する場合、どのデータに対しても機能することを保証することはできません):

select
  products.product_id,
  weeks.wkno,
  inventory.qoh,
  max(projection)
from forecast
join products on products.product_id = forecast.product_id
join weeks on weeks.wkno = forecast.for_week
left join inventory on
  inventory.product_id = products.product_id
  and inventory.asof_week = weeks.wkno
group by
  products.product_id,
  weeks.wkno,
  inventory.qoh

あまりアドバイスできなくてすみません。これが役立つことを願っています。

編集:クロス結合を削除するようにクエリを微調整しました。元のバージョンはこちら. 一部が欠落している場合に結合予測を残したい場合は、クロス結合が必要になる場合があります。あなたの特定の例では、それは不要です。

編集 2 : 上記のクエリは意味的に正しくありません。以下は正しいですが、私の主張を説明するものではありません。

select
  p.product_id,
  p.wkno,
  p.qoh,
  f.projection
from
  (select
      products.product_id,
      weeks.wkno,
      inventory.qoh,
      max(forecast.asof_week) max_p
    from forecast
    join products on products.product_id = forecast.product_id
    join weeks on weeks.wkno = forecast.for_week
    left join inventory on
      inventory.product_id = products.product_id
      and inventory.asof_week = weeks.wkno
    group by
      products.product_id,
          weeks.wkno,
      inventory.qoh) as p
  join forecast f on
    f.product_id = p.product_id
    and  f.for_week = p.wkno
    and f.asof_week = p.max_p
于 2013-04-29T05:01:05.420 に答える
1

データに欠落しているいくつかの PK/FK 制約があるようです:

CREATE TABLE products (
    product_id INTEGER PRIMARY KEY
    );
CREATE TABLE weeks (
    wkno INTEGER PRIMARY KEY
    );
CREATE TABLE inventory (
    product_id INTEGER REFERENCES products(product_id)
    , asof_week INTEGER REFERENCES weeks(wkno)
    , qoh float8
    , PRIMARY KEY (product_id,asof_week)
    );
CREATE TABLE forecast (
    product_id INTEGER REFERENCES products(product_id)
    , for_week INTEGER REFERENCES weeks(wkno)
    , asof_week INTEGER REFERENCES weeks(wkno)
    , projection FLOAT8
    , PRIMARY KEY (product_id,for_week,asof_week)
    );
INSERT INTO weeks VALUES (4),(5),(6),(7)
    , (1),(2),(3), (8) -- need these, too
    ;
-- et cetera.

weeksテーブルが「カレンダー」テーブルとして意図されている場合は、疑似テーブルに置き換えることができます (またそうする必要があります) generate_series(4,7)(そして FK 制約は削除されました)

クエリは、LEFT JOIN + MAX(aggregate) コンストラクトの影響を大きく受けます。以下は同じことを行う必要があり、より単純に見えます(NOT EXISTS救助のために...):

SELECT p.product_id "product"
        , i.asof_week "inven asof"
        , i.qoh "qoh"
        , f.for_week "fcast for"
        , f.projection "fcast qty"
        , f.asof_week "fcast asof"
FROM products p
CROSS JOIN weeks w
LEFT JOIN inventory i ON i.product_id = p.product_id AND i.asof_week = w.wkno
LEFT JOIN forecast f ON f.product_id = p.product_id AND f.for_week = w.wkno
WHERE NOT EXISTS (
    SELECT * FROM forecast f2
    WHERE f2.product_id = f.product_id
      AND f2.for_week = f.for_week
    AND  f2.asof_week < f.asof_week
    )
AND COALESCE(i.asof_week,f.for_week) IS NOT NULL
ORDER BY p.product_id, i.asof_week, f.for_week, f.asof_week
    ;
于 2013-04-29T08:14:46.853 に答える
0

ヒントをくれたジュリアンに感謝します。これで結果が得られますが、これが最善のアプローチであるかどうか、またはまだおもちゃのデータセットを使用しているため、1億行以上になったときにどうなるかはわかりません。おそらく最初の悪い点は、インデックスが作成されていないことですpw

with pw as ( select * from products, weeks )         
    select pw.product_id "product",
            pw.wkno,         
            i.asof_week "inven asof",    
            coalesce(i.qoh::text,'missing') "qoh",   
            f.for_week "fcast for",       
            coalesce(f.projection::text,'no fcast') "fcast qty",      
            f.asof_week "fcast asof"     
    from pw      
        left join inventory i on(pw.product_id = i.product_id and pw.wkno = i.asof_week )
        left join forecast f on(pw.product_id = f.product_id  
                                and f.for_week = pw.wkno          
                                and f.asof_week = (select max(f2.asof_week)               
                                                from forecast f2                          
                                                where f2.product_id = pw.product_id                       
                                                    and f2.asof_week < pw.wkno                            
                                                    and f2.for_week = pw.wkno))                               
    where        
        not (i.asof_week is null and f.asof_week is null)     
    order by pw.product_id,  
                pw.wkno,     
                f.for_week,                          
                f.asof_week           

利回り

 product | wkno | inven asof |   qoh   | fcast for | fcast qty | fcast asof
---------+------+------------+---------+-----------+-----------+------------
       1 |    4 |            | missing |         4 | 12        |          3
       1 |    5 |          5 | 10      |         5 | 31        |          4
       1 |    6 |          6 | 20      |         6 | 42        |          5
       1 |    7 |            | missing |         7 | 16        |          6
       2 |    6 |          6 | 200     |         6 | 2000      |          5
       2 |    7 |            | missing |         7 | 2100      |          5
(6 rows)
Time: 2.999 ms
于 2013-04-29T07:22:08.873 に答える