Sql 在内部联接中,如何根据滑动条件从右侧的表中仅选择一行?
我有一组包含周、产品、库存和每周预测的表格,我想从中选择第X周产品库存和最新预测。但我就是无法控制SQL:Sql 在内部联接中,如何根据滑动条件从右侧的表中仅选择一行?,sql,postgresql,postgresql-9.2,Sql,Postgresql,Postgresql 9.2,我有一组包含周、产品、库存和每周预测的表格,我想从中选择第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_
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比较陌生,可以使用一些有用的指针
关于数据的一些注意事项:我还有几个其他数据表要连接,我从示例中省略了这些数据表,以便继续关注这个问题,其中至少有一个数据表在性质上与预测数量表类似,即每个产品x周有多个版本行。每个产品X周大约有100个预测行,所以在某些地方我也不得不担心效率。。。但首先是正确的结果
我使用的是postgresql 9.2
谢谢。在不了解数据模型其余部分的情况下,很难给出一般性的指针,但我应该说:我通常发现,当我将查询保持尽可能平坦时,查询更容易推理。此外,一旦我有一大堆空检查,我就会尝试向数据添加保证,或者围绕不同的根表重新透视查询 无论如何,它应该适用于您,尽管我不能保证它适用于任何数据,尤其是在存在重复数据的情况下:
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
谢谢朱利安的提示。这就得到了结果,尽管我不确定这是否是最好的方法,或者一旦我有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
数据中似乎缺少一些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_series4,7伪表替换。FK约束也被取消了
查询使用左JOIN+MAXaggregate构造。以下内容也应该这样做,并且看起来更简单,因为救援人员不存在这些内容
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
;
朱利安,谢谢,更近了。我需要的不是maxprojection,而是{projection:asof_week是最近的}。我知道你是如何重新安排连接的,这是非常有见地的+1BTW:这个问题的陈述非常完整。可能除了缺少的PK/FK之外,做得很好@朱利安:这是一个品味的问题。我倾向于使用1=1表示法,因为它更为明确,十字符号将东西隐藏在关键字后面,看起来就像另一个杂音词。顺便说一句:我没有编辑你的空格,是吗?哦,好吧,很公平。至于空白,我不知道你的意思是什么:如果是我应该发表评论而不是编辑,那么我道歉。这并不重要,但我倾向于将别人的作品基本上视为一件艺术品,或者至少是一种表达自己的方式。即使我不理解它或者它会与我的偏好冲突。空白是一个笑话
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
;