【问题标题】:Selection of cheapest products, for a variable quantity (hourly demand)选择最便宜的产品,数量可变(每小时需求)
【发布时间】:2021-04-30 09:56:07
【问题描述】:

我们有两张桌子:

  • 一个由众多供应商投标(价格和数量),每小时 年。
  • 第二张表格列出了每小时(数量)的消费者总需求

我们需要生成:

  • 第三个表格列出了所有最便宜的供应商出价,但仅针对总消费者需求的数量。例如,虽然数量 (1) 可能有 10 个投标,但如果该小时的需求等于 (5),则只会选择最便宜的 5 个。

只有产品,它是同质的。因此,唯一相关的参数是价格和数量。价格是每单位数量的价格(即不是全部数量的总和)。

例子:

供应商出价(提供数量)

Hour Supplier Price Quantity
1 Siemens 1 10
1 Siemens 4 20
1 Fuji 7 20
2 ABB 1 10
2 Hitachi 2 15
2 Siemens 4 10

总需求

Hour Quantity
1 10
2 25

那么生成的表格应该是这样的:

中标(最便宜,总计需求)

Hour Supplier Price Quantity
1 Siemens 1 10
2 ABB 1 10
2 Hitachi 2 15

如您所见,此表中所有出价的总和等于第二个表中的总需求。即使有额外的出价,这些也不会包含在此处,因为需求已得到满足。

通常,供应商投标的总和不会完全满足总需求,例如,如果需求为 100,但每个数量等于 30。在这种情况下,添加到表中的最后一个投标的数量是“截止”(即,仅从该出价中获取满足需求的数量)。

我们考虑通过 PLSQL 来处理这个问题,遵循以下步骤:

  1. 我们创建了一个 CURSOR,它选择特定小时内的所有出价,按价格排序
  2. 一个循环将出价插入新表,直到插入的出价(数量)总和达到该小时的总需求。
  3. 通过在每小时(10 年的时间,意味着大约 90k 次迭代)循环中重新声明 CURSOR 来重复该过程。

由于我们是简单的经济学家,在 SQL 方面的背景有限,这似乎是处理这个问题的最直接的方法。然而,这种方法似乎非常耗费资源。即有超过 1500 万行供应商投标,循环时间约为 90 000。

因此,我们想知道你们中比我们受过更多 SQL 教育的人是否会对我们的方法提出任何建议。例如。 - 这是处理这个问题的唯一方法吗?有没有更好、更有效的方法?

【问题讨论】:

  • 价格是每件商品的价格,还是总价?换句话说,price=10, quantity=20 是否优于 price=5, quantity=5?如果价格是“总”价格,你会在“切断”时修改价格吗?换句话说,如果你有price=9, quantity=30而你只需要10个,那会变成price=9, quantity=10还是price=3, quantity=10?如果需求下降未填满,是否会“延续”到下一小时?
  • (i) 价格为单位数量,(ii) 切断时价格未修改,(iii) 未满足的需求确实不会结转

标签: sql oracle plsql


【解决方案1】:

假设...

  • 未满足的需求不会延续到下一个小时
  • price 是价格per item(不是整批的总价)
  • “截断”时,只为支付要求的数量(不是原投标中的数量)

这将算法简化为简单地以 price 的升序聚合出价,直到 sum(quantity) 超过 demand

这可以使用分析函数(也称为窗口函数)而不是循环来解决。


例如...

WITH
  hourly_cumulative AS
(
  SELECT
    demand.hour,
    demand.quantity    AS demand,
    bid.supplier,
    bid.price,
    bid.quantity       AS supply,
    SUM(bid.quantity)
      OVER (
        PARTITION BY demand.hour
            ORDER BY bid.price
      )
        AS fulfilled_demand
  FROM
    demand
  LEFT JOIN
    bid
      ON bid.hour = demand.hour
)
SELECT
  hour,
  demand,
  supplier,
  price,
  supply  AS potential_supply,
  CASE
    WHEN fulfilled_demand > demand
    THEN supply - (fulfilled_demand - demand)
    ELSE supply
  END
    AS actual_supply
FROM
  hourly_cumulative
WHERE
  fulfilled_demand - supply < demand

确保您在hour 上有索引,对于出价,最好至少在hour, price 上。

  • 索引将使连接更便宜
  • 如果它们与 window 子句中的 PARTITION 和 ORDER 匹配,它们也会降低成本

【讨论】:

  • 我们将在接下来的几天内对此进行试验,并就结果提供反馈。非常感谢您的帮助,非常感谢。
  • 也许已经有一点:价格实际上是单价(不是整个数量),但我知道这是查询实际假设的?如果没有,您能否说明需要更改的内容?
  • @BartDeMuynck - 另一个假设呢?如果您接受(price=5, quantity=30) 的出价并且只想要10 个单位*(“cutting off”),您会支付5*10=50 还是支付5*30=150?这对您想要/需要的算法有非常明显的影响。
  • 抱歉,我现在明白你的意思了。供应商按单价*实际数量获得报酬,因此在该示例中为 5*10。
  • @BartDeMuynck 那么当前代码可以满足您的需求,无需更改。
【解决方案2】:

我不确定您的表的名称,所以我称它们为 bidsdemands

select
   b.Hour
  ,b.Supplier
  ,b.Price
  ,b.Quantity-greatest(0,(b.running_total_quantity-d.quantity)) as quantity
from
    (
      select 
         Hour,Supplier,Price,Quantity
        ,nvl(
          sum(Quantity)
            over( partition by Hour 
                  order by Price 
                  rows between unbounded preceding and 1 preceding
                ) 
         ,0) as running_total_quantity
      from bids
    ) b
    join demands d
      on  b.hour=d.hour
      and b.running_total_quantity<d.Quantity
order by b.hour,b.price,b.running_total_quantity

如您所见,我们按价格计算每行的运行总数量,直到该行订单,但不包括当前行的数量。然后我们将这些结果与demandshour 结合起来,直到我们获得所需的数量。

带有测试数据的完整测试用例:

-- test data:
with
 bids(Hour,Supplier,Price,Quantity) as (
    select 1, 'Siemens' , 1, 10 from dual union all
    select 1, 'Siemens' , 4, 20 from dual union all
    select 1, 'Fuji'    , 7, 20 from dual union all
    select 2, 'ABB'     , 1, 10 from dual union all
    select 2, 'Hitachi' , 2, 15 from dual union all
    select 2, 'Siemens' , 4, 10 from dual
)
,demands(Hour,Quantity) as (
    select 1, 10 from dual union all
    select 2, 25 from dual
)-- end of test data
--main query
select
   b.Hour
  ,b.Supplier
  ,b.Price
  ,b.Quantity-greatest(0,(b.running_total_quantity-d.quantity)) as quantity
from
    (
      select 
         Hour,Supplier,Price,Quantity
        ,nvl(
          sum(Quantity)
            over( partition by Hour 
                  order by Price 
                  rows between unbounded preceding and 1 preceding
                ) 
         ,0) as running_total_quantity
      from bids
    ) b
    join demands d
      on  b.hour=d.hour
      and b.running_total_quantity<d.Quantity
order by b.hour,b.price,b.running_total_quantity

结果:

      HOUR SUPPLIER       PRICE   QUANTITY
---------- --------- ---------- ----------
         1 Siemens            1         10
         2 ABB                1         10
         2 Hitachi            2         15

【讨论】:

  • 我们将在接下来的几天内对此进行试验,并就结果提供反馈。非常感谢您的帮助,非常感谢。
猜你喜欢
  • 2022-09-30
  • 1970-01-01
  • 1970-01-01
  • 2015-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-25
  • 1970-01-01
相关资源
最近更新 更多