SQL ORACLE 从多个日期时间行中获取周数
Posted
技术标签:
【中文标题】SQL ORACLE 从多个日期时间行中获取周数【英文标题】:SQL ORACLE Get week numbers from multiple datetime rows 【发布时间】:2017-06-03 16:20:04 【问题描述】:我有 70.000 行数据,包括一个日期时间列 (YYYY-MM-DD HH24-MM-SS.)。 我想将此数据拆分为 3 个单独的列;小时、日期和周数。
日期时间列名称是“CONTRACTS”表中的“REGISTRATIONDATE”。
这是我迄今为止的日期和小时列:
SELECT substr(REGISTRATIONDATE, 0, 10) AS "Date",
substr(REGISTRATIONDATE, 11, 9) AS "Hour"
FROM CONTRACTS;
我已经看到了获取特定日期的周数的选项,这个任务涉及 70.000 个日期,所以这不是一个选项。
【问题讨论】:
日期的“周数”没有标准定义。你是什么意思?不过,Oracle 有自己的理解……如果您想要的好答案已经提供了。 How to extract week number in sql的可能重复 使用“周数”我的意思是一年有 52 周,我想有一个列来说明适合日期的周数(例如今天 2017/06/03 是周数22). 提供的答案可能就足够了,但还要检查我链接的可能重复项。很大程度上取决于您如何定义第 1 周。它是从 1 月 1 日开始,还是从 1 月的第一个星期一开始或其他什么...尝试各种选项,如果它们都不起作用,然后再回来更准确地说明您的需求。跨度> 我已经尝试了大约 20 种不同的选项来说明如何做到这一点(我正在学习 SQL),每次我得到适合您需要特定日期的周数的选项时。我有 70.000 个日期,所以可能不太方便。我所说的“周数”是指第 1 周从 1 月的第一个星期一开始。此时我已经有了“小时”和“日”列。我想创建一行代码,让我在第三行显示周数,适合日期。我希望这更准确。 【参考方案1】:您(OP)仍然需要解释为一年中的前几天分配的星期数,直到一年的第一个星期一。您是否为上一个日历年分配了周数?在评论中,我以 2017 年 1 月 1 日为例;那是一个星期天。根据您的定义,2017 年 1 月 2 日至 1 月 8 日这一周是“第 1 周”;您为 2017 年 1 月 1 日星期日分配的星期几?
下面的简单计算为其分配了周数 0。除此之外,计算很简单。
注意:要查找任何给定日期dt
的一周中的星期一,我们可以使用trunc(dt, 'iw')
。 iw 代表 ISO 周,标准周,从星期一开始,到星期日结束。
然后:要找到一年中的第一个星期一,我们可以从 1 月 7 日开始,并询问 1 月 7 日所在的那一周的星期一。 (我不会解释那个 - 这是简单的逻辑,它与编程无关。)
要输入固定日期,最好的方法是使用日期文字语法:date '2017-01-07'
表示 1 月 7 日。如果您不熟悉“日期文字”,请查看 Oracle 文档。
所以:要找到任何日期dt
的周数,计算
1 + ( trunc(dt, 'iw') - trunc(date '2017-01-07', 'iw') ) / 7
此公式查找dt
的 ISO 周的星期一并减去一年中的第一个星期一 - 使用 Oracle 日期算法,其中两个日期之间的差是它们之间的天数。所以要找到我们除以 7 的周数;并且要将第一个星期一分配为数字 1,而不是 0,我们需要将除以 7 的结果加 1。
您必须解决的另一个问题是将字符串转换为日期。最好的解决方案是修复数据模型本身(将列的数据类型更改为DATE
而不是VARCHAR2
);然后可以更轻松地提取您需要的所有数据位,您将确保您的数据中没有像“2017-02-29 12:30:00”这样的日期(目前,如果有,您将拥有进行任何日期计算都非常困难),查询会更快,等等。无论如何,这是一个完全不同的问题,所以我将把它排除在讨论之外。
【讨论】:
啊,好多了!我会删除我的答案,但有关查找不良数据的建议可能仍然有用。 +1 在此基础上,如果没有更改表格的选项,那么REGISTRATIONDATE
确实是一个日期时间字段,如果您对数据问题进行排序并且乐于为 1 月之前的日期分配第 0 周一月的第一个星期一,您需要的代码是:1 + (trunc(to_date(REGISTRATIONDATE,'YYYY-MM-DD HH24-MI-SS'), 'iw') - trunc(to_date(substr(REGISTRATIONDATE,1,4) || '-01-07','YYYY-MM-DD'), 'iw') ) / 7
@SteveLovell - 关于使用 to_date()
而不是日期文字的要点;如果 OP 有不同年份的日期,则计算应基于每个日期的特定年份;而日期文字就是这样,一个文字 - 它不能使用表达式,其中表达式的值是年份。【参考方案2】:
假设您的 REGISTRATIONDATE 格式为“MM/DD/YYYY”
简单(和更快)查询基于 to_char(to_date(REGISTRATIONDATE,'MM/DD/YYYY'),'WW') (否则将您的列转换为适当的日期并执行转换为周数)
SELECT substr(REGISTRATIONDATE, 0, 10) AS "Date",
substr(REGISTRATIONDATE, 11, 9) AS "Hour",
to_char(to_date(REGISTRATIONDATE,'MM/DD/YYYY'),'WW') as "Week"
FROM CONTRACTS;
【讨论】:
当我尝试to_char(to_date(REGISTRATIONDATE,'MM/DD/YYYY'),'WW') as "Week"
时,我收到错误 ORA-01830: date format picture ends before converting entire input string
这取决于您在注册日期中的格式...在我的回答中,我假设您的格式为“MM/DD/YYYY”...但我不知道您的真实格式
如果您的字段是日期时间,那么您应该只需要to_char(REGISTRATIONDATE,'WW')
我的字段确实是日期时间,当我只用to_char(REGISTRATIONDATE, 'WW')
尝试它时,不幸的是,我得到了错误ORA-1722: invalid number
。
尝试删除时间部分 TO_DATE(TO_CHAR(REGISTRATION , 'DD/MM/YYYY'), 'DD/MM/YYYY') ..【参考方案3】:
这很混乱,但看起来它可以工作:
to_char(
to_date(RegistrationDate,'YYYY-MM-DD HH24-MI-SS') +
to_number(to_char(trunc(to_date(RegistrationDate,'YYYY-MM-DD HH24-MI-SS'),'YEAR'),'D'))
- 2,
'WW')
在外面,您有其他人之前给出的解决方案,但使用了正确的日期格式。中间有一定天数的调整,以适应 1 月 1 日的落点。 trunc
部分从日期获取一月的第一天,'D'
获取一月 1 日的工作日。由于 1
代表星期日,我们必须使用 -2
来获取我们需要的内容。
编辑:我以后可能会删除这个答案,但在我看来,来自@mathguy 的答案是最好的。另请参阅该答案上的 cmets,了解如何扩展到通用解决方案。
但首先你需要:
-
在 1 月的第一个星期一之前决定要做什么,并且
解决日期中阻止其转换为日期的潜在问题。
在第 1 点,如果分配第 0 周是不可接受的(您想要第 52/53 周),它会变得有点复杂,但我们仍然可以提供帮助。
在我看来,在第 2 点上,要么存在系统性错误(也许它们是时间戳,包括几分之一秒),要么存在无效数据的孤立案例。
不计算长度、格式或特定值。您收到的错误消息表明至少有一些数据“太长”,我评论中的代码应该可以帮助您找到它。
【讨论】:
只要有机会,一定要告诉我这是怎么回事。如果事实证明它不太正确,我相信我们将能够修复它。或者,如果它满足您的需求,请将我的回答标记为“已接受”,以表示感谢并帮助其他用户找到最有用的信息。 感谢您的帮助。我试过这个答案,但得到了错误ORA-01830: date format picture ends before converting entire input string
。
我认为这意味着您的数据或至少一行数据没有'YYYY-MM-DD HH24-MI-SS'
格式的字段。
如果数据看起来大多是正确的格式,您可能需要运行 select * from CONTRACTS where length(REGISTRATIONDATE)<>length('YYYY-MM-DD HH24-MI-SS');
来检索所有“坏”数据。
根据数据问题的不同,另一种选择可能是将两次出现的to_date(RegistrationDate,'YYYY-MM-DD HH24-MI-SS')
替换为to_date(substr(RegistrationDate,1,10),'YYYY-MM-DD')
。以上是关于SQL ORACLE 从多个日期时间行中获取周数的主要内容,如果未能解决你的问题,请参考以下文章