【问题标题】:Using Dates in Oracle SQL在 Oracle SQL 中使用日期
【发布时间】:2022-01-20 15:03:00
【问题描述】:

我试图在我的数据库中只获取八月的月份,然后计算八月份有多少次表现,但我不知道该怎么做。

我已经给出了到目前为止我创建的代码。

SELECT f.FILM_NAME, COUNT(p.PERFORMANCE_DATE), SUM(p.TAKINGS), p.PERFORMANCE_DATE
FROM A2_PERFORMANCE p, A2_FILM f 
WHERE p.PERFORMANCE_DATE LIKE TO_DATE('08-2021', 'MM-YY')
GROUP BY f.FILM_NAME, p.PERFORMANCE_DATE
ORDER BY f.FILM_NAME

我目前正在努力实现这一目标:

-- FILM_NAME                   Performances Total Takings         
-- --------------------------- ------------ ----------------------
-- It Happened One Night       39               £63,571 
-- Modern Times                38               £58,332 
-- Parasite                    23               £37,195 
-- Knives Out                  22               £34,362 
-- Citizen Kane                25               £32,711 
-- The Wizard of Oz            18               £21,716 
-- Avengers: Endgame           18               £17,081

【问题讨论】:

  • 首先你需要检查TO_DATE('08-2021', 'MM-YY')的输出。然后你可以想象自己是一个 DBMS 并决定,你将如何对待一个日期“像”其他日期。最后使用some_date >= date '2021-08-01' and some_date < date '2021-09-01',因为在Oracle 中datedatetime 的误导性名称,between 将过滤掉最后一天午夜之后的日期。
  • 您的问题标题中有 SQL Developer。但是这个问题与它无关; SQL Developer 只是您使用的图形界面,问题不在于它。相反,问题是关于 Oracle SQL 的。我相应地编辑了你的标题。

标签: sql oracle date-arithmetic


【解决方案1】:

您可以将日期转换为字符串并将它们与“08-2021”进行比较(不是按照您的方式 - 您必须先将格式应用于日期本身),但这效率低。

您还可以将日期截断到月初并与日期'2021-08-01' 进行比较,但这也是低效的。

“效率低下”有两个来源,一个较小,一个非常大。较小的一个是必须将函数应用于表中的数据。真正重要的是与索引有关:如果您的查询经常根据日期进行过滤,那么您将从索引日期列中受益 - 但如果您的过滤器首先将函数应用于日期列,则无法使用索引。

那么,该怎么做呢?最好的(尤其是如果您的“日期”可能包含除午夜以外的时间部分)是这样的:

...
where p.performance_date >= date '2021-08-01'
  and p.performance_date <  date '2021-09-01'
...

注意它总是两个不等式,第一个是非严格的,第二个是严格的。

如果您改为使用performance_date between [Aug 1] and [Sep 1](在伪代码中),这将给出错误的答案 - 这使得第二个不等式也不是严格的,因此您将包括日期为 9 月 1 日的表演,如果他们是以午夜的时间保存在数据库中。 between [Aug 1] and [Aug 31] 将错过 8 月 31 日的所有时间,除了午夜之外。

最好不要使用 BETWEEN,而是使用两个明确的不等式,正如我所展示的那样。

【讨论】:

  • 您好,如果您想看看并提供帮助,我已经编辑了我的问题,以便更准确地说明我想要实现的目标。谢谢
  • @PeterNokes - 我回答了你关于如何过滤日期的问题。您的编辑不会改变我的答案。现在,您可能不喜欢您的查询没有产生所需的结果。那是一个单独的问题。快速浏览一下,我发现您在GROUP BY 子句中包含了执行日期。这是没有意义的。从那里删除它,看看是否能满足您的需求。
  • @PeterNokes - 然后,您还必须从 SELECT 中删除日期(它也没有业务:查询是针对月份的,而不是针对单个日期的)。
【解决方案2】:

你目前正在做的事情:

WHERE p.PERFORMANCE_DATE LIKE TO_DATE('08-2021', 'MM-YY')

您提供了一个 4 位数的年份值 (2021),但在格式模型中只给出了 YY,而不是 YYYY。现在,默认情况下,Oracle 对这类事情很宽容——有些人可能会说,这是无益的——所以这在这种情况下会起作用。但这不是一个好习惯,TO_DATE('08-2021', 'MM-YYYY') 会更好。无论哪种方式,这都会在该月的第一天给你午夜。您可以使用 date literal: DATE '2021-08-01' 更简单地提供这一点。

LIKEa pattern-matching condition,比较字符串,不是日期。您正在使用会话的 NLS_DATE_FORMAT 设置强制对列值和提供给字符串的固定日期进行隐式转换。您也没有包含任何通配符,使其等同于=。因此,使用常见的 NLS 设置,您确实在做:

WHERE TO_CHAR(p.PERFORMANCE_DATE, 'DD-MON-RR') = TO_CHAR(DATE '2021-08-01', 'DD-MON-RR')

使用该 NLS 设置,您将匹配 8 月 1 日的任何值 - 尽管它会匹配 1921 年等以及 2021 年的数据(如果有的话)。使用更精确的 NLS 设置,您可能会这样做:

WHERE TO_CHAR(p.PERFORMANCE_DATE, 'YYYY-MM-DD HH24:MM:SS') = TO_CHAR(DATE '2021-08-01', 'YYYY-MM-DD HH24:MI:SS')

仅匹配 8 月 1 日午夜的值。

要匹配整个月份,您可以使用显式掩码,而不是隐式转换;并且您只需要转换列值,因为无论如何您都可以以您想要的格式提供固定值:

WHERE TO_CHAR(p.PERFORMANCE_DATE, 'YYYY-MM') = '2021-08'

或者,如果您愿意(我不喜欢,但这不是我的代码):

WHERE TO_CHAR(p.PERFORMANCE_DATE, 'MM-YYYY') = '08-2021'

但是这种转换会阻止使用该列上的正常索引。我建议提供一个日期范围以避免任何转换:

WHERE p.PERFORMANCE_DATE >= DATE '2021-08-01'
AND p.PERFORMANCE_DATE < DATE '2021-09-01'

它将查找该列在 8 月 1 日午夜或之后以及 9 月 1 日午夜之前的所有行。

【讨论】:

  • 您好,如果您想看看并提供帮助,我已经编辑了我的问题,以便更准确地说明我想要实现的目标。谢谢
【解决方案3】:

如果您正在查找 8 月的所有活动,无论年份如何,您都可以将您的列转换为 'MON' 的 char 并将其与 'AUG' 匹配:

SELECT f.FILM_NAME, COUNT(p.PERFORMANCE_DATE), SUM(p.TAKINGS),p.PERFORMANCE_DATE
FROM A2_PERFORMANCE p, A2_FILM f 
WHERE TO_CHAR(p.PERFORMANCE_DATE,'MON') ='AUG'
GROUP BY f.FILM_NAME, p.PERFORMANCE_DATE
ORDER BY f.FILM_NAME

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-28
    • 2011-11-09
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 1970-01-01
    • 2013-04-09
    相关资源
    最近更新 更多