与在 Oracle 中使用 TO_CHAR 比较相比,在前几个月的数据的 select 语句中使用 BETWEEN 是否有好处?

Is there a benefit to using BETWEEN in a select statement for a previous months data vs using a comparison with TO_CHAR in Oracle?

我正在修改 Oracle 中的查询,其中需要上个月的数据。目前,查询有一个 where 子句,内容如下:

...
...
...
WHERE cust_dt BETWEEN TO_DATE('05-01-2022','mm-dd-yyyy) AND TO_DATE('05-31-2022', 'mm-dd-yyyy')

我正在修改查询,这样开始日期和结束日期就不需要每个月都手动更改为 运行 查询。经过一番研究,我得出以下结论:

...
...
...
WHERE TO_CHAR(cust_dt, 'MM-YYYY') = TO_CHAR(add_months(sysdate, -1), 'MM-YYYY')

我得到的结果是我想要的,但我很好奇,如果数据集更大,哪个查询的性能会更好。我在网上看到的所有帖子都使用BETWEEN,所以我想知道是否有什么原因。

对于调优、测试、性能等问题,我完全是个菜鸟。实际查询本身相当复杂,有多个连接,因此性能很重要。目前,我只有少量的测试数据可以使用,所以我能做的是找到最好的结果。

回到我的问题,哪个查询最好?使用 BETWEEN 的那个,或者使用 TO_CHAR?

的那个

我建议不要使用您的两个查询之一。第一个查询的问题是它正在检查硬编码的日期范围。这通常是一种应尽可能避免的风险。第二个查询不需要从 date 到 char 的转换,这可能会很慢。

您可以只使用常用的日期函数来获取上个月的数据。 根据你的描述,你的参考日期是当前日期,即sysdate。

为了排除因时间原因比较不正确,可以使用函数TRUNC从日期中去掉时间。

函数ADD_MONTHS可以回溯一个月

函数LAST_DAY可用于查找该月的最后一天。

综合起来,您可以使用这样的 where 子句:

WHERE cust_dt BETWEEN ADD_MONTHS(TRUNC(sysdate,'mm'),-1) AND 
LAST_DAY(ADD_MONTHS(TRUNC(sysdate,'mm'),-1));

这将快速安全地执行,并避免不必要的转换或硬编码日期。 最后一点:考虑将上述查询更改为...

WHERE cust_dt BETWEEN ADD_MONTHS(TRUNC(sysdate,'mm'),-1)
AND TRUNC(sysdate,'MM')-INTERVAL '0.001' SECOND;

...取决于您是否需要上个月的最后一天。 请在这里查看差异并尝试:db<>fiddle

WHERE TO_CHAR(cust_dt, 'MM-YYYY') = TO_CHAR(add_months(sysdate, -1), 'MM-YYYY')

不会在 cust_dt 列上使用索引;相反,您需要在 TO_CHAR(cust_dt, 'MM-YYYY')

上创建一个单独的 function-based 索引
WHERE cust_dt BETWEEN TO_DATE('05-01-2022', 'mm-dd-yyyy')
                  AND TO_DATE('05-31-2022', 'mm-dd-yyyy')

将在 cust_dt 列上使用索引。

而在Oracle中,一个DATEdata-type由7个字节组成,分别代表:世纪、year-of-century、月、日、时、分、秒。它 ALWAYS 具有这些组件(但通常您用来访问数据库的客户端应用程序将默认仅显示日期组件而不显示时间组件 - 但时间组件仍然存在)。

这意味着您的查询将查找 cust_dt 介于 2022-05-01 00:00:002022-05-31 00:00:00 之间的值。它不会匹配 cust_dt2022-05-31 00:00:012022-05-31 23:59:59 之间的值。

So to circle back to my question, which query would be best?

都没有。

你想要(如果你硬编码日期):

WHERE cust_dt >= TO_DATE('05-01-2022', 'mm-dd-yyyy')
AND   cust_dt <  TO_DATE('06-01-2022', 'mm-dd-yyyy')

或者(如果您动态查找日期):

WHERE cust_dt >= ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -1)
AND   cust_dt <  TRUNC(SYSDATE, 'MM')

这将在 cust_dt 上使用索引并匹配月份的整个范围。

db<>fiddle here