PostgreSQL和多个匹配行

时间:2018-09-17 07:49:40

标签: sql postgresql

我正在制定一种汽车统计解决方案,需要每公里行驶收费。

我有下表:

table: cars
columns: car_id, km_driven

table: pricing
columns: from, to, price

我的cars表中的内容可以是:

car_id, km_driven
2, 430
3, 112
4, 90

pricing表上的内容可以是:

from, to, price
0, 100, 2
101, 200, 1
201, null, 0.5

意思是,前100公里的价格为每公里2美元,接下来的100公里的价格为每公里1美元,而以上所有里程的价格为每公里0.5美元。

是否有通过cars为我的PostgreSQL计算费用的逻辑和简单方法?

因此,如果car驱动了ex。 201,那么价格将是100x2 + 100x1 + 0.5,而不是201x0.5

5 个答案:

答案 0 :(得分:2)

根据@ sean-johnston的答案进行修改:

select 
  car_id, km_driven, 
  sum(case 
    when km_driven>=start then (least(finish,km_driven)-start+1)*price 
    else 0
  end) as dist_price
from cars,pricing
group by car_id,km_driven
  • 保留原始范围
  • 其中km_driven> =开始省略了(可选,但可能会提高性能)

摆弄多一点,在适当的位置可以省略大小写

select 
  car_id, km_driven, 
  sum((least(finish,km_driven)-start+1)*price) as dist_price
from cars,pricing
where km_driven >= start
group by car_id,km_driven

dbfiddle

答案 1 :(得分:2)

我将查询写为:

select c.car_id, c.km_driven, 
   sum(( least(p.to_km, c.km_driven) - p.from_km + 1) * p.price) as dist_price
from cars c join
     pricing p
     on c.km_driven >= p.from_km
group by c.car_id, c.km_driven;

这里是db<>fiddle

答案 2 :(得分:1)

我将肯定使用程序来做到这一点,因为可以使用循环以非常直接的方式实现它。但是,您应该可以执行以下操作:

select car_id, sum(segment_price)
from (
  select 
  car_id, 
  km_driven, 
  f, 
  t, 
  price, 
  driven_in_segment, 
  segment_price
  from (
      select 
      car_id, 
      km_driven, 
      f, 
      t, 
      price, 
      (coalesce(least(t, km_driven), km_driven) - f) driven_in_segment, 
      price * (coalesce(least(t, km_driven), km_driven) - f) segment_price
      from 
      -- NOTE: cartesian product here
      cars, 
      pricing
      where f < km_driven
  )
) data
group by car_id
order by car_id

不过,我发现它的可读性很差。

更新:

该查询比必要的要复杂一些,我正在尝试使用一些窗口函数来完成一些最终不需要的事情。这里的等效简化版本:

select car_id, sum(segment_price)
from (
  select 
  car_id, 
  km_driven, 
  f, 
  t, 
  price, 
  (coalesce(least(t, km_driven), km_driven) - f) driven_in_segment, 
  price * (coalesce(least(t, km_driven), km_driven) - f) segment_price
  from 
  -- NOTE: cartesian product here
  cars, 
  pricing
  where f < km_driven
) data
group by car_id
order by car_id

答案 3 :(得分:1)

明智地使用大小写/和组合。但是,首先需要使范围一致。我选择将第一个范围更改为1100。鉴于此,以下内容应会给您希望。 (我还使用了“开始” /“结束”,因为“从/到”是保留字)。

select
  car_id, km_driven,
  sum (case
    when finish is null and km_driven >= start
      then (km_driven-start+1) * price
    when km_driven >= start
      then (case
              when (km_driven - start + 1) > finish
                then (finish - start + 1)
              else (km_driven - start + 1)
              end) * price
    else 0
    end) as dist_price
from cars, pricing
where km_driven >= start
group by 1, 2;

说明:

  1. 我们加入了旅程至少到该范围起点的任何范围。
  2. 开放式范围在第一个case子句中处理,非常简单。
  3. 我们需要一个封闭的范围内子句,因为我们只希望行程中的一部分在该范围内。
  4. 然后将总行程价格的结果相加。

如果您不想(或无法)使范围保持一致,那么您需要为起始范围添加第三个外壳。

答案 4 :(得分:0)

您可以使用join并通过

时的用例来计算成本
    select c.car_id, case when p.price=.5 
   then  100*2+100*1+(c.km_driven-200)*0.5 
    when   p.price=1 then 100*2+(c.km_driven-100)*1
    else c.km_driven*p.price as cost
   from cars c join pricing p
   on c.km_driven>=p.from and c.km_driven<=p.to