PostgreSQL在小时数约束下的两个时间戳之间获取随机值

时间:2018-09-22 18:02:51

标签: random postgresql-10 sql-timestamp

使用:PostgreSQL 10.5

这个问题有点类似于:

PostgreSQL Get a random datetime/timestamp between two datetime/timestamp

answer given by @pozs解决了这个问题,但没有让我限制返回的随机时间戳中的小时数。我要说这是此问题的延伸。

任务

区别在于我需要在两个时间戳之间获取一个随机时间戳,但是输出值中的小时必须介于{{1}之间}和10:00:00

我的尝试

我一直在尝试高效地做到这一点,但目前仅想到了存储日期,时间和毫秒的不同部分,然后使用18:00:00将它们与3个选择组合在一起的想法。但是,这远不是一个快速的解决方案。

ORDER BY random() LIMIT 1保留日期,tmp_data保留时间,tmp_time保留毫秒,我使用函数将其加在一起以获取正确的输出:

tmp_ms

这项工作已经完成,但由于要对3个选择进行排序的预计算表进行选择(因此需要为每一行进行计算),因此需要一些时间。

示例数据/说明

鉴于以下时间限制:

  • start_timestamp => (SELECT data FROM tmp_data ORDER BY random() LIMIT 1) + (SELECT czas FROM tmp_time WHERE czas BETWEEN '10:00:00' AND '18:00:00' ORDER BY random() LIMIT 1) + (SELECT ms FROM tmp_ms ORDER BY random() LIMIT 1)
  • end_timestamp => 2016-01-01 10:00:00

让我们为每个部分生成随机时间戳,但是小时(小时必须在10到18之间)。

样本输出-随机生成

2017-12-31 18:00:00

这里的每个小时都在10到18之间,但是时间戳的每个其他部分都是随机的。

2 个答案:

答案 0 :(得分:1)

将输出从this answer转换为date类型,然后添加10:00:00时间(较低的小时限制)和random间隔最多8小时(较高的小时限制)很快就完成了:

select 
  date (timestamp '2016-01-01' + 
        random() * (timestamp '2017-12-31' - timestamp '2016-01-01'))
 + time '10:00:00' 
 + random() * INTERVAL '8 hours';

答案 1 :(得分:0)

如果您使用EXTRACT(epoch FROM <YOUR TIMESTAMP>)将时间戳转换为秒,则可以执行以下操作:

  1. random()给出介于0和1之间的值
  2. 如果您将随机值乘以时间戳记差,则会得到可能的范围(10到18小时,范围将在0到8小时之间)
  3. 在范围内添加起点会将计算的值转换为起点和终点之间的值。现在,您有了以秒为单位的预期随机时间戳记
  4. 使用to_timestamp
  5. 将秒转换回Postgres时间戳
  6. 执行两次:一次获取随机日期,一次获取随机时间。
  7. 添加两个时间戳(一个时间戳转换为日期,一个时间戳转换为时间)。

查询

WITH timestamps AS (

    SELECT 
        EXTRACT(epoch FROM start::time) start_time,
        EXTRACT(epoch FROM "end"::time) end_time,
        EXTRACT(epoch FROM start::date) start_date,
        EXTRACT(epoch FROM "end"::date) end_date
    FROM (
        SELECT
            '2016-01-01 10:00:00'::timestamp as start,
            '2017-12-31 18:00:00'::timestamp as end
    ) s
)

SELECT 
    to_timestamp(
        random() * (ts.end_date - ts.start_date) + ts.start_date 
    )::date + 
    to_timestamp(
        random() * (ts.end_time - ts.start_time) + ts.start_time 
    )::time 
FROM timestamps ts

demo:db<>fiddle