Oracle 函数 return VALUES 之间的随机日期和时间戳
Oracle function to return random date and timestamp between VALUES
我在创建一个 returns 随机日期(下一个是 returns 随机时间戳的函数)的函数时遇到了一些困难,当在开始和结束日期范围内传递时。
我希望该值还包括与日期关联的随机时间。对于时间戳函数,一个随机小数值。
以下是我目前拥有的 DATE 函数,但我似乎无法编译它。任何帮助将不胜感激。感谢所有回答的人。
CREATE OR REPLACE FUNCTION random_date(
p_from IN DATE,
p_to IN DATE
)
RETURN date DETERMINISTIC
IS
v_start DATE := TRUNC(LEAST(p_from, p_to));
v_end DATE := TRUNC(GREATEST(p_from, p_to));
RETURN p_from
+ DBMS_RANDOM.VALUE(0, p_to - p_from + 1);
END random_date;
/
它:
- 缺少
BEGIN
关键字;
- 不希望它成为
DETERMINISTIC
(因为它不会成为):
CREATE FUNCTION random_date(
p_from IN DATE,
p_to IN DATE
) RETURN DATE
IS
c_from CONSTANT DATE := LEAST(p_from, p_to);
c_to CONSTANT DATE := GREATEST(p_from, p_to);
BEGIN
RETURN c_from + TRUNC(DBMS_RANDOM.VALUE() * ((c_to - c_from) * 86400 + 1))
/ 86400;
END random_date;
/
并且,对于 TIMESTAMP
秒:
CREATE FUNCTION random_timestamp(
p_from IN TIMESTAMP,
p_to IN TIMESTAMP
) RETURN TIMESTAMP
IS
c_from CONSTANT TIMESTAMP(9) := LEAST(p_from, p_to);
c_to CONSTANT TIMESTAMP(9) := GREATEST(p_from, p_to);
BEGIN
RETURN c_from + DBMS_RANDOM.VALUE()
* (c_to + INTERVAL '0.000000001' SECOND - c_from);
END random_timestamp;
/
注意:DBMS_RANDOM.VALUE()
会 return 一个大于或等于 0 且小于 1 的值,所以如果你想在range 那么您需要以尽可能小的增量增加范围;因此为日期增加 1 秒,为时间戳增加 1e-9 秒。
上面的 RANDOM_TIMESTAMP
函数有效但不是真正随机的,因为由于舍入问题,范围任一端的瞬间发生的概率是两个极端之间所有其他瞬间的一半。对于大多数情况,这不是一个特定的问题,但如果它是(特别是对于几微秒的小范围),那么您需要在应用随机性之前将间隔的持续时间转换为整数:
CREATE OR REPLACE FUNCTION random_timestamp(
p_from IN TIMESTAMP,
p_to IN TIMESTAMP
) RETURN TIMESTAMP
IS
c_from TIMESTAMP(9) := LEAST(p_from, p_to);
c_to TIMESTAMP(9) := GREATEST(p_from, p_to);
c_diff INTERVAL DAY(9) TO SECOND(9) := c_to - c_from;
c_sdiff NUMBER(38,0) := EXTRACT(DAY FROM c_diff) * 86400e6
+ EXTRACT(HOUR FROM c_diff) * 3600e6
+ EXTRACT(MINUTE FROM c_diff) * 60e6
+ EXTRACT(SECOND FROM c_diff) * 1e6
+ 1;
BEGIN
RETURN c_from + NUMTODSINTERVAL(
TRUNC(DBMS_RANDOM.VALUE() * c_sdiff) / 86400e6,
'DAY'
);
END random_timestamp;
/
db<>fiddle here
我无法为该范围的最后一天生成日期或时间戳,无论它在月份中的哪个位置。例如,random_date (DATE '2022-04-27', DATE '2022-05-03') 在该月的最后一天(4 月 30 日)而不是 5 月 3 日生成日期。
如果p_from是2022年4月1日午夜,p_to是2022年4月30日午夜,那么我需要生成一个0到30之间的随机数,但是(p_to - p_from) = 29,不是 30为 0,而不是 1。)如果我想要一个小于(从不等于)5 月 1 日午夜的随机日期,则将 5 月 1 日作为第二个参数传递给 random_date。为了自动执行此操作,我对函数进行了以下更改。
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'DD-MON-YYYY HH24:MI:SS.FF';
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS';
CREATE OR REPLACE FUNCTION random_date(
p_from IN DATE,
p_to IN DATE
) RETURN DATE
IS
BEGIN
RETURN p_from + DBMS_RANDOM.VALUE() * (p_to - p_from + 1);
END random_date;
/
CREATE OR REPLACE FUNCTION random_timestamp(
p_from IN TIMESTAMP,
p_to IN TIMESTAMP
) RETURN TIMESTAMP
IS
BEGIN
RETURN p_from + DBMS_RANDOM.VALUE() * (p_to - p_from + interval '1' day);
END random_timestamp;
/
CREATE TABLE t1 (
seq_num NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
dt DATE,
ts TIMESTAMP
);
INSERT INTO t1 (dt, ts )
SELECT
random_date(DATE '2022-04-01', DATE '2022-04-30'),
random_timestamp(DATE '2022-04-01', DATE '2022-04-30')
FROM
dual CONNECT BY level <= 10000;
select trunc(dt), count(*)
from t1
group by trunc(dt)
Order by trunc(dt);
select trunc(ts), count(*)
from t1
group by trunc(ts)
Order by trunc(ts);
我在创建一个 returns 随机日期(下一个是 returns 随机时间戳的函数)的函数时遇到了一些困难,当在开始和结束日期范围内传递时。
我希望该值还包括与日期关联的随机时间。对于时间戳函数,一个随机小数值。
以下是我目前拥有的 DATE 函数,但我似乎无法编译它。任何帮助将不胜感激。感谢所有回答的人。
CREATE OR REPLACE FUNCTION random_date(
p_from IN DATE,
p_to IN DATE
)
RETURN date DETERMINISTIC
IS
v_start DATE := TRUNC(LEAST(p_from, p_to));
v_end DATE := TRUNC(GREATEST(p_from, p_to));
RETURN p_from
+ DBMS_RANDOM.VALUE(0, p_to - p_from + 1);
END random_date;
/
它:
- 缺少
BEGIN
关键字; - 不希望它成为
DETERMINISTIC
(因为它不会成为):
CREATE FUNCTION random_date(
p_from IN DATE,
p_to IN DATE
) RETURN DATE
IS
c_from CONSTANT DATE := LEAST(p_from, p_to);
c_to CONSTANT DATE := GREATEST(p_from, p_to);
BEGIN
RETURN c_from + TRUNC(DBMS_RANDOM.VALUE() * ((c_to - c_from) * 86400 + 1))
/ 86400;
END random_date;
/
并且,对于 TIMESTAMP
秒:
CREATE FUNCTION random_timestamp(
p_from IN TIMESTAMP,
p_to IN TIMESTAMP
) RETURN TIMESTAMP
IS
c_from CONSTANT TIMESTAMP(9) := LEAST(p_from, p_to);
c_to CONSTANT TIMESTAMP(9) := GREATEST(p_from, p_to);
BEGIN
RETURN c_from + DBMS_RANDOM.VALUE()
* (c_to + INTERVAL '0.000000001' SECOND - c_from);
END random_timestamp;
/
注意:DBMS_RANDOM.VALUE()
会 return 一个大于或等于 0 且小于 1 的值,所以如果你想在range 那么您需要以尽可能小的增量增加范围;因此为日期增加 1 秒,为时间戳增加 1e-9 秒。
上面的 RANDOM_TIMESTAMP
函数有效但不是真正随机的,因为由于舍入问题,范围任一端的瞬间发生的概率是两个极端之间所有其他瞬间的一半。对于大多数情况,这不是一个特定的问题,但如果它是(特别是对于几微秒的小范围),那么您需要在应用随机性之前将间隔的持续时间转换为整数:
CREATE OR REPLACE FUNCTION random_timestamp(
p_from IN TIMESTAMP,
p_to IN TIMESTAMP
) RETURN TIMESTAMP
IS
c_from TIMESTAMP(9) := LEAST(p_from, p_to);
c_to TIMESTAMP(9) := GREATEST(p_from, p_to);
c_diff INTERVAL DAY(9) TO SECOND(9) := c_to - c_from;
c_sdiff NUMBER(38,0) := EXTRACT(DAY FROM c_diff) * 86400e6
+ EXTRACT(HOUR FROM c_diff) * 3600e6
+ EXTRACT(MINUTE FROM c_diff) * 60e6
+ EXTRACT(SECOND FROM c_diff) * 1e6
+ 1;
BEGIN
RETURN c_from + NUMTODSINTERVAL(
TRUNC(DBMS_RANDOM.VALUE() * c_sdiff) / 86400e6,
'DAY'
);
END random_timestamp;
/
db<>fiddle here
我无法为该范围的最后一天生成日期或时间戳,无论它在月份中的哪个位置。例如,random_date (DATE '2022-04-27', DATE '2022-05-03') 在该月的最后一天(4 月 30 日)而不是 5 月 3 日生成日期。
如果p_from是2022年4月1日午夜,p_to是2022年4月30日午夜,那么我需要生成一个0到30之间的随机数,但是(p_to - p_from) = 29,不是 30为 0,而不是 1。)如果我想要一个小于(从不等于)5 月 1 日午夜的随机日期,则将 5 月 1 日作为第二个参数传递给 random_date。为了自动执行此操作,我对函数进行了以下更改。
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'DD-MON-YYYY HH24:MI:SS.FF';
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS';
CREATE OR REPLACE FUNCTION random_date(
p_from IN DATE,
p_to IN DATE
) RETURN DATE
IS
BEGIN
RETURN p_from + DBMS_RANDOM.VALUE() * (p_to - p_from + 1);
END random_date;
/
CREATE OR REPLACE FUNCTION random_timestamp(
p_from IN TIMESTAMP,
p_to IN TIMESTAMP
) RETURN TIMESTAMP
IS
BEGIN
RETURN p_from + DBMS_RANDOM.VALUE() * (p_to - p_from + interval '1' day);
END random_timestamp;
/
CREATE TABLE t1 (
seq_num NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
dt DATE,
ts TIMESTAMP
);
INSERT INTO t1 (dt, ts )
SELECT
random_date(DATE '2022-04-01', DATE '2022-04-30'),
random_timestamp(DATE '2022-04-01', DATE '2022-04-30')
FROM
dual CONNECT BY level <= 10000;
select trunc(dt), count(*)
from t1
group by trunc(dt)
Order by trunc(dt);
select trunc(ts), count(*)
from t1
group by trunc(ts)
Order by trunc(ts);