是否可以通过 SQL(oracle)中的截断时间戳进行排序?

Posted

技术标签:

【中文标题】是否可以通过 SQL(oracle)中的截断时间戳进行排序?【英文标题】:Is it possible to order by truncated timestamp in SQL (oracle)? 【发布时间】:2021-12-29 19:29:09 【问题描述】:

我有一个用例,我想按时间订购,但要按一定的分辨率。例如,我的模式将时间戳保存到小数点后 9 位(纳秒精度),但我只想按分钟排序并使用不同的字段在该分钟内排序。我试过这个

select * from myTable order by (cast(myTimeStamp at time zone 'UTC' as timestamp) - to_timestamp('01-JAN-01'))/1000000000*60 desc, id desc;

将时间戳转换为纪元,然后除以得到分钟精度。但这给出了错误的顺序。此外,当我对上述命令进行转储以了解返回的数据类型时,我看到数据类型:typ=190,但我找不到 in the oracle docs 类型,这让我更加困惑。

所以我想知道我错过了什么?应该可以通过截断(到分钟)的时间戳进行排序,不胜感激。

【问题讨论】:

【参考方案1】:

TIMESTAMP WITH TIME ZONE 转换为UTC 时区,以便您可以比较相同的时间,然后TRUNC将其返回到分钟的开头:

SELECT *
FROM   myTable
ORDER BY
       TRUNC(myTimeStamp at time zone 'UTC', 'MI') DESC,
       id DESC;

其中,对于样本数据:

CREATE TABLE myTable(
  id          NUMBER,
  myTimestamp TIMESTAMP WITH TIME ZONE
);

INSERT INTO myTable(id, myTimestamp)
SELECT 1, TIMESTAMP '1970-01-01 00:00:00 UTC'              FROM DUAL UNION ALL
SELECT 2, TIMESTAMP '1970-01-01 00:00:00 America/New_York' FROM DUAL UNION ALL
SELECT 3, TIMESTAMP '1970-01-01 00:00:00 Asia/Hong_Kong'   FROM DUAL UNION ALL
SELECT 4, TIMESTAMP '1970-01-01 00:00:00 Europe/Paris'     FROM DUAL UNION ALL
SELECT 5, TIMESTAMP '1970-01-01 01:00:00 UTC'              FROM DUAL UNION ALL
SELECT 6, TIMESTAMP '1970-01-01 01:00:00 America/New_York' FROM DUAL UNION ALL
SELECT 7, TIMESTAMP '1970-01-01 01:00:00 Europe/Berlin'    FROM DUAL;

输出:

ID MYTIMESTAMP
6 01-JAN-70 01.00.00.000000 AMERICA/NEW_YORK
2 01-JAN-70 00.00.00.000000 AMERICA/NEW_YORK
5 01-JAN-70 01.00.00.000000 UTC
7 01-JAN-70 01.00.00.000000 EUROPE/BERLIN
1 01-JAN-70 00.00.00.000000 UTC
4 01-JAN-70 00.00.00.000000 EUROPE/PARIS
3 01-JAN-70 00.00.00.000000 ASIA/HONG_KONG

如果您想查看在排序过程中使用的转换为 UTC 的值,只需将其添加到输出中:

SELECT t.*,
       TO_CHAR(TRUNC(myTimeStamp at time zone 'UTC', 'MI'), 'YYYY-MM-DD HH24:MI:SS')
         AS converted_ts
FROM   myTable t
ORDER BY
       TRUNC(myTimeStamp at time zone 'UTC', 'MI') DESC,
       id DESC;

哪些输出:

ID MYTIMESTAMP CONVERTED_TS
6 01-JAN-70 01.00.00.000000 AMERICA/NEW_YORK 1970-01-01 06:00:00
2 01-JAN-70 00.00.00.000000 AMERICA/NEW_YORK 1970-01-01 05:00:00
5 01-JAN-70 01.00.00.000000 UTC 1970-01-01 01:00:00
7 01-JAN-70 01.00.00.000000 EUROPE/BERLIN 1970-01-01 00:00:00
1 01-JAN-70 00.00.00.000000 UTC 1970-01-01 00:00:00
4 01-JAN-70 00.00.00.000000 EUROPE/PARIS 1969-12-31 23:00:00
3 01-JAN-70 00.00.00.000000 ASIA/HONG_KONG 1969-12-31 16:00:00

如果您只使用TRUNC 而不转换为公共时区,那么它将根据日期和时间组件进行排序,而不考虑时区的相对差异。

db小提琴here

【讨论】:

【参考方案2】:

你为什么不把时间戳截断为分钟?

SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:Mi:ss';

Session altered.

SQL> select systimestamp col_1,
  2         trunc(systimestamp, 'mi') col_2
  3  from dual;

COL_1                                    COL_2
---------------------------------------- -------------------
29.12.21 20:32:50,178000 +01:00          29.12.2021 20:32:00

SQL>

那么你会

order by trunc(timestamp_column, 'mi'),
         yet_another_column                

【讨论】:

这似乎有效,但我的查询作为应用程序的一部分运行。因此,使用这种方法,我需要为每个数据库连接正确更改会话? 否;我更改它只是为了设置格式(因为默认情况下,它设置为 DD.MM.YY,所以你不会看到任何区别)。您只需要 ORDER BY 子句。

以上是关于是否可以通过 SQL(oracle)中的截断时间戳进行排序?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Oracle 中将日期截断为秒

oracle ORA-06502:PL/SQL:数字或值错误:批量绑定:截断绑定

从 oracle-sql 中的时间戳日期列中提取日期

ansi sql如何选择自表中最早时间戳以来的天数

SQL Server 2008 中的截断/清除表变量

oracle时间戳6转换为sql server datetime2错误