我在 Vertica 中有一张表
a
,其中包含 item_id
附加到 code
的时间 from_date
create table a (
code varchar(16),
item_id int,
from_date timestamptz
);
和一个表格
b
,其中包含给定日期的 code
t
create table b (
code varchar(16),
t date
);
我需要在
a.item_id
日期从 b.code
检索 b.t
。数据看起来像这样:
insert into a values ('abc', 1, '2013-01-01 10:00:00 EST'::timestamptz);
insert into a values ('abc', 2, '2013-01-05 11:00:00 EST'::timestamptz);
insert into a values ('abc', 3, '2013-01-09 9:00:00 EST'::timestamptz);
insert into b values ('abc', '2013-01-06'::date);
我尝试在 LEFT JOIN 中使用 Vertica 的 INTERPOLATE,以便在
(code, item_id)
时检索前一个 t
对
select
b.code, b.t, a.item_id
from b
left join a on a.code = b.code and b.t INTERPOLATE PREVIOUS VALUE a.t;
这给了我一个类型错误
Interpolated predicates can accept arguments of the same type only
。
所以我尝试将
b.t
转换为时间戳
select
b.code, b.t, a.item_id
from b
left join a on a.code = b.code
and (b.t::timestamptz) INTERPOLATE PREVIOUS VALUE a.t;
这给了我一个语法错误
Syntax error at or near "INTERPOLATE"
。
如果没有 INTERPOLATE,SQL 表达式可以工作,但不会连接上一行。
是否可以在 JOIN 和 INTERPOLATE 中将我的日期转换为时间戳?
如果没有,是否有其他方法可以让我在时间
item_id
检索当前的 t
?
我发现了一个巧妙的解决方法,通过欺骗解释器并在 SELECT 子查询中进行日期到时间戳的转换:
select
b.code, b.t, a.item_id
from b
left join (select a.t::date, a.code, a.item from a) a on a.code = b.code
and b.t INTERPOLATE PREVIOUS VALUE a.t;
select b.code, b.t, a.item_id
from b
left join a on a.code = b.code and b.t = cast(a.t as date);
但是,当“a”表中有超过 1 行,其中 t 包含同一日期的不同时间时,此解决方案可能是错误的。
例如,
insert into a values ('abc', 4, '2013-01-06 8:00:00 EST'::timestamptz);
insert into a values ('abc', 5, '2013-01-06 9:00:00 EST'::timestamptz);
在这种情况下,您应该决定需要哪一行。 比如说,您需要最新行:
select b.code, b.t, a.item_id
from b
left join (
select a1.code, a1.item_id, a2.dt
from a a1
inner join (
select code, cast(t as date) dt, max(t) as max_t
from a
group by code, cast(t as date)
) a2 on a1.code = a2.code and a1.t = a2.max_t
) a on a.code = b.code and b.t = a.dt;
解决方案可能会有所不同,具体取决于您的 DBMS。
虽然
INTERPOLATE
不支持强制转换和表达式,但允许使用子查询,并且可以使用子查询将 DATE
强制转换为 TIMESTAMPTZ
。然而,这种简单的方法似乎行不通,经过一番尝试,我得到了以下要求:
WITH
b_ext AS (
SELECT
code,
t, -- This is a DATE
t::TIMESTAMPTZ + '0 S'::INTERVAL AS dt
FROM b
)
SELECT
b.code, b.t, a.item_id
FROM
b_ext b
LEFT JOIN a
ON a.code = b.code AND b.dt INTERPOLATE PREVIOUS VALUE a.t;
不知何故,转换
t::TIMESTAMPTZ
还不够,确实需要添加零INTERVAL
,至少在Vertica 11.0中是这样。