简体   繁体   English

如何计算Oracle中两个日期之间的of年数?

[英]How to calculate number of leap years between two dates in Oracle?

I want to calculate the number of leap years between the hire date of the employee and the current date (on hr.employees table in Oracle SQL Developer). 我想计算雇员的雇用日期和当前日期之间的of年数(在Oracle SQL Developer中的hr.employees表中)。 How to do this? 这个怎么做?

A leap year consists of 366 days. 年包括366天。 I assume a "leap year" between two dates consists of all the days from Jan 1 to Dec 31 of a year with Feb 29th. 我假设两个日期之间的“ le年”由一年的1月1日至12月31日(即2月29日)的所有日期组成。

Based on this understanding, there is a pretty simple solution. 基于这种理解,有一个非常简单的解决方案。

  • Count the number of days between the Jan 1 of the year following the hire date and Jan 1 of the year of the end date. 计算雇用日期后一年的1月1日到结束日期一年的1月1日之间的天数。
  • Count the number of years between those two dates. 计算这两个日期之间的年数。
  • Subtract the difference between the days and the number of years * 365 减去天数与年数之间的差* 365

Happily, built-in functions do most of the work. 幸运的是,内置功能可以完成大部分工作。

This results in: 结果是:

   (trunc(sysdate, 'YYYY') -
    trunc(hiredate + interval '1' year, 'YYYY') -
    365 * (extract(year from sysdate) - extract(year from hiredate) - 1)
   ) as num_years

It is a little trickier to count leap days but that is not what the question is asking. 计算leap日数有点棘手,但这不是问题要问的。

You can reuse the code Oracle has already written: just check if creating a leap day raises an exception: 您可以重用Oracle已经编写的代码:只需检查创建a日是否引发异常:

SELECT TO_DATE('2016-02-29','YYYY-MM-DD') FROM DUAL;
29.02.2016 

but

SELECT TO_DATE('2018-02-29','YYYY-MM-DD') FROM DUAL;
ORA-01839: date not valid for month specified

So you just have to count the exceptions: 因此,您只需要计算例外:

CREATE OR REPLACE FUNCTION count_leap_years (p_from DATE, p_to DATE) RETURN NUMBER 
IS
  number_of_leap_days NUMBER := 0;
  date_not_valid EXCEPTION;
  PRAGMA EXCEPTION_INIT(date_not_valid, -1839);
BEGIN
  FOR y IN EXTRACT(YEAR FROM p_from) .. EXTRACT(YEAR FROM p_to) LOOP
    DECLARE
      d DATE;
    BEGIN
      d := TO_DATE(to_char(y,'fm0000')||'-02-29', 'YYYY-MM-DD');
      IF p_from < d AND d < p_to THEN
        number_of_leap_days := number_of_leap_days + 1;
      END IF;
    EXCEPTION WHEN date_not_valid THEN 
      NULL;
    END;
  END LOOP;
  RETURN number_of_leap_days;
END count_leap_years;
/

Something like this will allow you to select numbers, in this case, from 1 to 2999. 在这种情况下,您可以选择1到2999之间的数字。

    Select Rownum year
    From dual
    Connect By Rownum <= 2999

Something like this will allow you to check if a specific year is a leap year or not 这样的事情将允许您检查特定年份是否为a年

CASE WHEN ((MOD(YEAR, 4) = 0 AND (MOD(YEAR, 100) <> 0)) OR MOD(year, 400) = 0) THEN 1 ELSE 0 END as isLeapYear

Now you just need to add the filtering and the sum of the leap years. 现在,您只需要添加过滤和the年的总和。

Select sum(isLeapYear)
from (
    Select year, CASE WHEN ((MOD(YEAR, 4) = 0 AND (MOD(YEAR, 100) <> 0)) OR MOD(year, 400) = 0) THEN 1 ELSE 0 END as isLeapYear
FROM (
    Select Rownum year
    From dual
    Connect By Rownum <= 2999
    ) a
    Where a.year >= EXTRACT(YEAR FROM DATE %DateStart%) and a.year <= EXTRACT(YEAR FROM DATE %DateEnd%)
) b

This can be really improved in terms of performance, but like this I think its easier do understand what each step is doing and then you can decompose it into what you really want and expect. 就性能而言,这确实可以得到改善,但是像这样,我认为它更容易理解每​​个步骤在做什么,然后您可以将其分解为您真正想要和期望的东西。

try this: 尝试这个:

CREATE OR REPLACE FUNCTION LEAP_YEARS(EMP_ID IN NUMBER)
RETURN NUMBER
IS 

HIRE_YEAR NUMBER:=0;
SYS_YEAR  NUMBER:=0;
NUMBER_LEAP_YEARS NUMBER:=0;

BEGIN
 SELECT EXTRACT(YEAR FROM HIRE_DATE) INTO HIRE_YEAR
 FROM EMPLOYEE
 WHERE EMPLOYEE_ID = EMP_ID;

 SELECT EXTRACT(YEAR FROM SYSDATE()) INTO SYS_YEAR
 FROM DUAL;

 FOR IDX IN HIRE_YEAR..SYS_YEAR LOOP
    IF MOD(IDX,4)=0 THEN
        NUMBER_LEAP_YEARS := NUMBER_LEAP_YEARS  + 1;
    END IF;
 END LOOP;

RETURN NUMBER_LEAP_YEARS;
END;

I tested it on a toy example and it works. 我在一个玩具示例上对其进行了测试,并且可以正常工作。 Maybe you need to improve it (hint: maybe it needs some exceptions and it assume that hire_date < sysdate). 也许您需要改进它(提示:也许它需要一些例外,并且它假设hire_date <sysdate)。

Then you can use it as: 然后,您可以将其用作:

SELECT LEAP_YEARS(E.EMPLOYEE_ID) FROM EMPLOYEE E;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM