Oracle SQL 将日期维度表与日期值上的另一个表连接起来

时间:2021-03-04 08:03:21

标签: sql oracle max left-join

我有一个包含所有日期的日期维度表和另一个包含特定日期项目值的表。 例如 (a) Date_Dim 表

|Full_Date  |  
|-----------|
| ....      |
|1-jan-2021 |
|2-Jan-2021 |
|3-jan-2021 |
| ...       |

(b) Item_value 表

|P_Date      | ITEM  | Value  |
|-----------:|:------|-------:|
|20-Dec-2020 |AA1    |9       |
|1-jan-2021  |AA1    |10      |
|1-jan-2021  |AA2    |100     |
| ...        | ...   | ...    |

我正在尝试构建一个事实表,其中包含 date_dim 表中每个日期的 item_value 表中每个项目的最新值。即每天物品的价值。 例如

|Full_date   | ITEM   | Value |
|-----------:|-------:|------:|
|31-Dec-2020 |AA1     | 9     |
|31-Dec-2020 |AA2     | null  |
|1-Jan-2021  |AA1     | 10    |
|1-Jan-2021  |AA2     | 100   |
|2-Jan-2021  |AA1     | 10    |
|2-Jan-2021  |AA2     | 100   |
|3-Jan-2021  |AA1     | 10    |
|3-Jan-2021  |AA2     | 100   |
|4-Jan-2021  |AA1     | 10    |
|4-Jan-2021  |AA2     | 100   |

请问如何建立这个查询? 我尝试了以下但不起作用

选择 full_date,p_date,item,value 从dim_date full_date=p_date 上的左外连接 item_value;

不确定 max(p_date) over (partition by ...) 是否有效。

谢谢

4 个答案:

答案 0 :(得分:0)

您可以使用分区外连接然后聚合:

WITH date_dim ( full_date ) AS (
  SELECT DATE '2020-12-31' + LEVEL - 1 AS full_Date
  FROM   DUAL
  CONNECT BY DATE '2020-12-31' + LEVEL - 1 <= DATE '2021-01-04'
)
SELECT item,
       full_date,
       MAX( value ) KEEP ( DENSE_RANK LAST ORDER BY p_date ) AS value
FROM   date_dim d
       LEFT OUTER JOIN item_value i
       PARTITION BY ( i.item )
       ON ( d.full_date >= i.p_date )
GROUP BY item, full_date

对于样本数据:

CREATE TABLE item_value ( P_Date, ITEM, Value ) AS
SELECT DATE '2020-12-20', 'AA1',   9 FROM DUAL UNION ALL
SELECT DATE '2021-01-01', 'AA1',  10 FROM DUAL UNION ALL
SELECT DATE '2021-01-01', 'AA2', 100 FROM DUAL;

输出:

<块引用>
ITEM | FULL_DATE | VALUE
:--- | :-------- | ----:
AA1  | 31-DEC-20 |     9
AA1  | 01-JAN-21 |    10
AA1  | 02-JAN-21 |    10
AA1  | 03-JAN-21 |    10
AA1  | 04-JAN-21 |    10
AA2  | 31-DEC-20 |  null
AA2  | 01-JAN-21 |   100
AA2  | 02-JAN-21 |   100
AA2  | 03-JAN-21 |   100
AA2  | 04-JAN-21 |   100

注意:不需要存储date_dim维度表;它可以即时生成,并将减少执行(昂贵的)IO 操作从硬盘读取表的需要。

db<>fiddle here

答案 1 :(得分:0)

您可以简单地使用分析函数 ITEM

为您的 LEAD 表添加一个有效间隔
select  
P_DATE,
lead(P_DATE-1,1,(select max(full_date) from date_dim)) over (partition by ITEM order by P_DATE) P_DATE_TO,
ITEM, VALUE
from item_value
;

P_DATE              P_DATE_TO           ITE      VALUE
------------------- ------------------- --- ----------
20.12.2020 00:00:00 31.12.2020 00:00:00 AA1          9
01.01.2021 00:00:00 04.01.2021 00:00:00 AA1         10
01.01.2021 00:00:00 04.01.2021 00:00:00 AA2        100

在某些情况下,这对于您的用例来说已经足够了,因为您可以使用

在给定的 VALUE 上查询特定 ITEMdate
select VALUE from item_value_hist h where ITEM = 'AA2' 
                                          and <query_date> BETWEEN h.P_DATE and h.P_DATE_TO

请注意,有效间隔包含,因为我们对于 P_DATE_TO 从相邻的 P_DATE 中减去一天。您应该注意 DATE 具有时间组件。

如果您想要 ITEM per DAY 概览,您必须首先添加 缺少的早期历史,其中 VALUENULL

select 
(select min(full_date) from date_dim) P_DATE,  min(P_DATE)-1  P_DATE_TO, ITEM, null VALUE
from item_value
group by ITEM
having min(P_DATE) > (select min(full_date) from date_dim)

P_DATE              P_DATE_TO           ITE VALUE
------------------- ------------------- --- -----
31.12.2020 00:00:00 31.12.2020 00:00:00 AA2 

比简单的外连接到您的维度表匹配从您的有效间隔

with item as (
select  
P_DATE,
lead(P_DATE-1,1,(select max(full_date) from date_dim)) over (partition by ITEM order by P_DATE) P_DATE_TO,
ITEM, VALUE
from item_value
union all
select 
/* add the missing early history without a VALUE */
(select min(full_date) from date_dim) P_DATE,  min(P_DATE)-1  P_DATE_TO, ITEM, null VALUE
from item_value
group by ITEM
having min(P_DATE) > (select min(full_date) from date_dim)
)
select dt.full_date, item.ITEM, item.VALUE from item
join date_dim dt
on dt.full_date between item.P_DATE and item.P_DATE_TO
order by item.ITEM, dt.full_date

FULL_DATE           ITE      VALUE
------------------- --- ----------
31.12.2020 00:00:00 AA1          9
01.01.2021 00:00:00 AA1         10
02.01.2021 00:00:00 AA1         10
03.01.2021 00:00:00 AA1         10
04.01.2021 00:00:00 AA1         10
31.12.2020 00:00:00 AA2           
01.01.2021 00:00:00 AA2        100
02.01.2021 00:00:00 AA2        100
03.01.2021 00:00:00 AA2        100
04.01.2021 00:00:00 AA2        100

答案 2 :(得分:0)

两步:

  1. 交叉加入日期和项目。如果您没有项目表(您应该有),请从您的 item_value 表中加入不同的项目。
  2. 使用 FROM 获取 OUTER APPLY 子句中的值,或使用 SELECT 使用子查询获取 FETCH FIRST ROW ONLY 子句中的值。

查询:

select 
  d.full_date,
  i.item,
  (
    select iv.value
    from Item_value iv
    where iv.item = i.item
    and iv.p_date <= d.full_date
    order by iv.p_date desc
    fetch first row only
  ) as value
from dim_date d
cross join (select distinct item from item_value) i
order by d.full_date, i.item;

答案 3 :(得分:0)

您可以使用 cross join 后跟 left join 以引入现有值来生成日期和项目的完整列表。然后您可以使用 last_value()lag() 来填充值:

select d.p_date, i.item, 
       coalesce(v.value,
                lag(v.value ignore nulls) over (partition by i.item order by d.p_date)
               ) as value
from date_dim d cross join
     (select distinct iv.item from item_value iv) i left join
     item_value iv
     on iv.p_date = d.p_date and iv.item = i.item;

您也可以使用 join 通过向值表添加“结束”日期来执行此操作:

select d.p_date, i.item, 
       coalesce(v.value,
                lag(v.value ignore nulls) over (partition by i.item order by d.p_date)
               ) as value
from date_dim d cross join
     (select distinct iv.item from item_value iv) i left join
     (select iv.*,
             lead(p_date) over (partition by item order by p_date) as next_p_date
      from item_value iv
     ) iv
     on i.item = iv.item and
        d.p_date >= iv.p_date and
        (iv.next_p_date is null or d.p_date < iv.next_p_date);