如何计算 table SQL Vertica 前后的天数?

How to calculate days to and after in table SQL Vertica?

我有 SQL table 如下所示:

date        col1
2020-01-02  xxx

我有一些特殊的约会,比如:2020-01-01, 2020-01-05, 2020-05-10
我需要计算到最近的特殊日期的天数和最后一个特殊日期之后的天数,所以我需要如下结果:

next_special = 3 因为最近的特殊日期 2020-01-02 是 2020-01-05(3 天)
last_special = 1 因为 2020-01-02 的最后一个特殊日期是 1 天前 (2020-01-01)

date        col1  next_special  last_special
2020-01-02  xxx   3             1

如果我没理解错的话,您可以将复杂的 case 表达式与 least()greatest() 一起使用:

select t.*,
       nullif( least(case when '2020-01-01' < date then datediff(day, '2020-01-01', date) else 999999 end,
                     case when '2020-01-05' < date then datediff(day, '2020-01-05', date) else 999999 end,
                     case when '2020-05-10' < date then datediff(day, '2020-05-10', date) else 999999 end
                    ), 999999
             ) as prev_special,
       nullif( least(case when '2020-01-01' > date then datediff(day, date, '2020-01-01') else 999999 end,
                     case when '2020-01-05' > date then datediff(day, date, '2020-01-05') else 999999 end,
                     case when '2020-05-10' > date then datediff(day, date, '2020-05-10') else 999999 end
                    ), 999999
             ) as next_special
from t;

我认为这在几乎任何其他数据库中表达起来都会更简单——因为它们支持 SELECT 子句中的相关标量子查询。

编辑:

如果您在单独的 table 中有这些值,并且您在原始 table 中有一个唯一 ID,您可以使用:

select t.date, t.col1,
       min(case when d.date < t.date then datediff(day, d.date, t.date end) as prev_special,
       min(case when d.date > t.date then datediff(day, t.date, d.date end) as next_special
from t cross join
     dates d
group by t.date, t.col1;

更容易表达。性能方面更差。

Vertica 使用 事件系列连接 ,它是使用 LEFT JOIN 子句中的 INTERPOLATE PREVIOUS VALUE 谓词获得的。您不是在 LEFT JOIN 中使用 NULLS,而是从紧邻的前一行获取数据。

而且 Vertica 非常擅长 OLAP 函数 - 以及管道并行性,因此嵌套多个查询通常会减少一些伤害。

在下面的示例中,我在两个限制日期中创建了一系列 15 个连续日期,从 1 月 1 日到 1 月 15 日,将 'xxx' 添加为 indata 中的 col1,并加入a specdays table(在用其后续日期丰富每一行之后),然后应用事件系列连接,并进行数学计算。

这就是你想要的吗?

WITH
specdays(dt) AS (
          SELECT DATE '2020-01-01' -- new year's day
UNION ALL SELECT DATE '2020-01-03' -- sunday
UNION ALL SELECT DATE '2020-01-06' -- epiphany
UNION ALL SELECT DATE '2020-01-10' -- sunday
)
,
prevnext AS (
  SELECT
    dt
, LEAD(dt) OVER w AS nextdt
FROM specdays
WINDOW w AS (ORDER BY dt)
)
,
-- list of dates beween 1st Jan and 15th Jan
indata AS (
SELECT
  tms::DATE AS dt
, 'xxx'     AS col1
FROM (
          SELECT TIMESTAMP '2020-01-01' 
UNION ALL SELECT TIMESTAMP '2020-01-15' 
) limits(dt)
TIMESERIES tms AS '1 DAY' OVER(ORDER BY dt)
)
SELECT 
  indata.* 
, prevnext.dt     AS prevspecday
, prevnext.nextdt AS nextspecday
, TIMESTAMPDIFF('DAY',prevnext.dt    ,indata.dt  ) AS last_special
, CASE WHEN prevnext.dt = indata.dt
    THEN 0
    ELSE TIMESTAMPDIFF('DAY',indata.dt  ,prevnext.nextdt)
  END AS next_special
FROM indata
LEFT JOIN prevnext ON indata.dt INTERPOLATE PREVIOUS VALUE prevnext.dt
;

dt        |col1|prevspecday|nextspecday|last_special|next_special
2020-01-01|xxx |2020-01-01 |2020-01-03 |           0|           0
2020-01-02|xxx |2020-01-01 |2020-01-03 |           1|           1
2020-01-03|xxx |2020-01-03 |2020-01-06 |           0|           0
2020-01-04|xxx |2020-01-03 |2020-01-06 |           1|           2
2020-01-05|xxx |2020-01-03 |2020-01-06 |           2|           1
2020-01-06|xxx |2020-01-06 |2020-01-10 |           0|           0
2020-01-07|xxx |2020-01-06 |2020-01-10 |           1|           3
2020-01-08|xxx |2020-01-06 |2020-01-10 |           2|           2
2020-01-09|xxx |2020-01-06 |2020-01-10 |           3|           1
2020-01-10|xxx |2020-01-10 |-          |           0|           0
2020-01-11|xxx |2020-01-10 |-          |           1|-
2020-01-12|xxx |2020-01-10 |-          |           2|-
2020-01-13|xxx |2020-01-10 |-          |           3|-
2020-01-14|xxx |2020-01-10 |-          |           4|-
2020-01-15|xxx |2020-01-10 |-          |           5|-