1

在 SQL 表 person_rate 中,我们存储了一个随时间变化的速率浮点值。列:

id (serial, PK)
person_id (int)
date_from (date)
rate (float)

(person_id, date_from)是独一无二的,因为每天最多允许更改一次(可能是 PK,但这并不重要)

给定 person_id 的速率值在时间域内有效,从date_fromdate 到下一个 succesive 记录date_from,或者如果没有这样的记录,则到无限未来。例子:

id   person_id  date_from  rate
101  1          2011-01-01 100.0
145  1          2012-01-01 180.0
193  1          2012-05-01 140.0

现在我需要一个 SELECT 查询,它对每个给定的 $date都person_id返回一个有效值。rate假设 2012-03-01 是 180;2012-05-02 是 140 等等。

我测试过的解决方案:

1)条件date_from <= $date+使用窗口函数rank() OVER (PARTITION BY person_id ORDER BY date_from DESC+超选WHERE rank = 1

2) 类似于 1) 但使用SELECT DISTINCT ON (person_id)而不是将等级限制为 1

1) 和 2) 都表现不佳,EXPLAIN 表明 db 需要对每个记录进行排序person_id,然后首先限制为 1。可能这种类型的查询不能充分利用索引date_from

IDEA - 添加一个date_to列,这会有点多余,因为该值将是“连续记录的 date_from,减去 1 天”(如果没有连续记录,则为 +infty)。但是随后的查询可能是date_from <= $date AND date_to >= $date- 这可能会对 date_from 和 date_to 上的索引产生良好的性能。

但我有点害怕在这种情况下如何管理数据完整性 - 如何设置一个 person_id 的 [ date_from .. date_to ] 间隔不应该重叠的约束?

对于这种类型的查询,postgresql 的最佳解决方案是什么?负载是读取最多的,对 person_rate 表的写入不多。典型的查询内部需要获取每月每一天的费率......

也许这个使用 pg 9.2 上的新索引的索引/主键序数的 SQL 查询可以以某种方式提供帮助?

4

4 回答 4

0

使用这个简单的查询:

SELECT person_id, date_from, rate FROM person_rate where date_from in (SELECT MAX(date_from) FROM person_rate WHERE date_from <= 'provided Date' and person_id = provided_id) 

获取所有人员费率使用。

SELECT a.person_id, a.date_from, a.rate FROM person_rate a JOIN (SELECT person_id, MAX(date_from) as date_from FROM person_rate where date_from <= 'provided Date') b ON(a.date_from = b.date_from and a.person_id = b.person_id)
于 2013-04-09T12:04:03.323 回答
0

您可以使用LEAD生成to_date

with scd_table (
    select  a.*  , 
            lead(from_date,1,to_date('31/12/9999','dd/mm/yyyy')) over (partition by a order by from_date asc) as to_date
    from    YOUR_TABLE a
)
select  * 
from    scd_table
where   :d >= from_date
and     :d < to_date

(这是 oracle 语法,但是lead是 ANSI 标准)

我认为你应该重新考虑你的设计 - 寻找缓慢变化的维度表。有很多关于其他设计的文章。你这样做的方式 - 你总是会获取下一个 from_date 以便只获取一个特定的客户端实例,以便选择有点繁重。

于 2013-04-09T12:04:32.810 回答
0

SQL小提琴

select distinct on (person_id) person_id, date_from, rate
from person_rate
where date_from <= '2012-03-01'
order by person_id, date_from desc

如果 (person_id, date_from) 是唯一的,则创建该索引:

create table person_rate (
    id serial primary key,
    person_id int,
    date_from date,
    rate float,
    unique (person_id, date_from)
);

如果那已经是生产表,请更改它:

alter table person_rate add 
constraint constraint_name unique (person_id, date_from);

别忘了analyze person_rate在那之后跑。如果满足正确的条件,它将仅使用索引。这包括一张足够大的桌子。

于 2013-04-09T12:05:17.983 回答
0
SELECT person_id, rate FROM person_rate WHERE date_from <= '2012-05-02' ORDER BY date_from DESC LIMIT 0,1;

LIMIT 的第二个数字是您要使用的最大 person_id(例如,前 5 个 person_id 的 LIMIT 0,5)和日期在这里: date_from <= '2012-05-02'

于 2013-04-09T12:51:00.507 回答