如何在 firebird 函数 datediff 中获得正确的年、月和日

How to get correct year, month and day in firebird function datediff

我必须再问一个关于 Firebird 中的 datediff 的问题。在这种情况下,我不知道如何获得正确的结果: 工人 x 有两份雇佣合约,第一份是在 1988-09-15 至 2000-03-16 期间,第二份是从 2000-03-16 至 2005-02-28。我想要得到的结果是这样的 16 年 5 个月零 3 天,因为第一个结果是 11 年 6 个月零 1 天,第二个结果是 4 年 11 个月零 2 天。 有谁能告诉我如何在火鸟中做到这一点。大多数我想知道如何从几个月的总和(17 个月)我可以做 5 个月,其他 12 个月增加一年的价值。现在我有 SQL 这样的:

SELECT
a.id_contact, 
sum(floor(datediff(day, a.DATE_FROM, a.DATE_TO)/365.25)) as YEAR,
mod(sum(mod(floor(datediff(day, a.DATE_FROM, a.DATE_TO)/30.41),12)),12) as MTH
FROM KP a
group by a.id_contact

然后我得到 5 个月,但我没有 12 个月增加到年的价值。请帮助我...

先求和天数再求和再计算Y,M,D

SELECT
KP3.id_contact 
, (KP3.D2-KP3.D1) / (12*31) AS Y
, ((KP3.D2-KP3.D1) - ((KP3.D2-KP3.D1) / (12*31)) * 12 * 31) / 31 AS M
, CAST(MOD((KP3.D2-KP3.D1) - (((KP3.D2-KP3.D1) / (12*31)) * 12 * 31), 31) AS INTEGER) AS D
FROM
(SELECT
KP2.id_contact, SUM(KP2.D1) AS D1, SUM(KP2.D2) AS D2
FROM
    (
    SELECT
    KP.id_contact, DATEDIFF(MONTH, KP.DATE_FROM, KP.DATE_TO) / 12 AS Y, CAST(MOD(DATEDIFF(MONTH, KP.DATE_FROM, KP.DATE_TO), 12) AS INTEGER) AS M 
    , EXTRACT(YEAR FROM KP.DATE_FROM)*12*31+EXTRACT(MONTH FROM KP.DATE_FROM)*31+EXTRACT(DAY FROM KP.DATE_FROM) D1
    , EXTRACT(YEAR FROM KP.DATE_TO)*12*31+EXTRACT(MONTH FROM KP.DATE_TO)*31+EXTRACT(DAY FROM KP.DATE_TO) D2 
    FROM
    KP  
    ) AS KP2
GROUP BY KP2.id_contact
) AS KP3

正确的方法似乎是衡量在两项任务上花费的天数,然后将这些日期相加,然后将其转换为本质上不精确 给予或接受街头谈话-像年-月-日的形式。 下面有更多内容

借用Livius的conversion query,将系数调整得更逼真,会发展成这样:

https://dbfiddle.uk/?rdbms=firebird_3.0&fiddle=2fba0ace6a70ae16a167ec838642dc28

在这里,我们一步一步地移动,从简单的块构建越来越复杂的块,最终给了我们 16 年 5 个月零 2 天:

select rdb$get_context('SYSTEM', 'ENGINE_VERSION') as version from rdb$database;
| VERSION |
| :------ |
| 3.0.5   |
create table KP (
  ID_CONTACT integer not null,
  DATE_FROM date not null,
  DATE_TO date not null
)
-- 
create index KP_workers on KP(id_contact)
insert into KP values (1, '1988-09-15', '2000-03-16')
1 rows affected
insert into KP values (1, '2000-03-16', '2005-02-28')
1 rows affected
-- the sample data from 
-- might expose the rounding bug in my original formulae:
-- unexpected ROUNDING UP leading to NEGATIVE value for months

insert into KP values (2, '2018-02-08', '2019-12-01')
1 rows affected
insert into KP values (2, '2017-02-20', '2018-01-01')
1 rows affected
select a.*, datediff(day, a.DATE_FROM, a.DATE_TO) as DAYS_COUNT from KP a
ID_CONTACT | DATE_FROM  | DATE_TO    | DAYS_COUNT
---------: | :--------- | :--------- | :---------
         1 | 1988-09-15 | 2000-03-16 | 4200      
         1 | 2000-03-16 | 2005-02-28 | 1810      
         2 | 2018-02-08 | 2019-12-01 | 661       
         2 | 2017-02-20 | 2018-01-01 | 315       
-- Original answer by Livius

SELECT
KP3.id_contact 
, KP3.D2-KP3.D1 as days_count
, (KP3.D2-KP3.D1) / (12*31) AS Y
, ((KP3.D2-KP3.D1) - ((KP3.D2-KP3.D1) / (12*31)) * 12 * 31) / 31 AS M
, CAST(MOD((KP3.D2-KP3.D1) - (((KP3.D2-KP3.D1) / (12*31)) * 12 * 31), 31) AS INTEGER) AS D
FROM
(SELECT
KP2.id_contact, SUM(KP2.D1) AS D1, SUM(KP2.D2) AS D2
FROM
    (
    SELECT
    KP.id_contact, DATEDIFF(MONTH, KP.DATE_FROM, KP.DATE_TO) / 12 AS Y, CAST(MOD(DATEDIFF(MONTH, KP.DATE_FROM, KP.DATE_TO), 12) AS INTEGER) AS M 
    , EXTRACT(YEAR FROM KP.DATE_FROM)*12*31+EXTRACT(MONTH FROM KP.DATE_FROM)*31+EXTRACT(DAY FROM KP.DATE_FROM) D1
    , EXTRACT(YEAR FROM KP.DATE_TO)*12*31+EXTRACT(MONTH FROM KP.DATE_TO)*31+EXTRACT(DAY FROM KP.DATE_TO) D2 
    FROM
    KP  
    ) AS KP2
GROUP BY KP2.id_contact
) AS KP3
ID_CONTACT | DAYS_COUNT | Y  | M  |  D
---------: | :--------- | :- | :- | -:
         1 | 6120       | 16 | 5  | 13
         2 | 997        | 2  | 8  |  5
select ID_CONTACT, sum(DAYS_COUNT) as DAYS_COUNT
from (
  select a.*, datediff(day, a.DATE_FROM, a.DATE_TO) as DAYS_COUNT from KP a
)
GROUP BY 1
ID_CONTACT | DAYS_COUNT
---------: | :---------
         1 | 6010      
         2 | 976       
-- this step taken from https://dbfiddle.uk/?rdbms=firebird_3.0&fiddle=52c1e130f589ca507c9ff185b5b2346d

-- based on original Livius forumla with non-exact integer coefficients
-- it seems not be generating negative counts, but still shows very different results

SELECT
    KP_DAYS.id_contact,
    KP_DAYS.DAYS_COUNT / (12*31) AS Y,
    ((KP_DAYS.DAYS_COUNT) - ((KP_DAYS.DAYS_COUNT) / (12*31)) * 12 * 31) / 31 AS M,
    CAST(MOD((KP_DAYS.DAYS_COUNT) - (((KP_DAYS.DAYS_COUNT) / (12*31)) * 12 * 31), 31) AS INTEGER) AS D
FROM
(
  select ID_CONTACT, sum(DAYS_COUNT) as DAYS_COUNT
  from (
    select a.*, datediff(day, a.DATE_FROM, a.DATE_TO) as DAYS_COUNT from KP a
  )
  GROUP BY 1  
) as KP_DAYS
ID_CONTACT | Y  | M  |  D
---------: | :- | :- | -:
         1 | 16 | 1  | 27
         2 | 2  | 7  | 15
SELECT
    KP_DAYS.id_contact, KP_DAYS.days_count
  , FLOOR(KP_DAYS.DAYS_COUNT / 365.25) AS Y
  , FLOOR( (KP_DAYS.DAYS_COUNT - (FLOOR(KP_DAYS.DAYS_COUNT / 365.25) * 365.25) ) / 30.5) AS M 
  , CAST(MOD((KP_DAYS.DAYS_COUNT) - (((KP_DAYS.DAYS_COUNT) / 365.25) * 365.25), 30.5) AS INTEGER) AS D
FROM
(
  select ID_CONTACT, sum(DAYS_COUNT) as DAYS_COUNT
  from (
    select a.*, datediff(day, a.DATE_FROM, a.DATE_TO) as DAYS_COUNT from KP a
  )
  GROUP BY 1  
) as KP_DAYS
ID_CONTACT | DAYS_COUNT | Y  | M  |  D
---------: | :--------- | :- | :- | -:
         1 | 6010       | 16 | 5  |  2
         2 | 976        | 2  | 8  |  1

注意,以上在数学上仍然不正确。但是应该给出一个"gut feeling"的时间戳。 以 Y-M-D 形式获得准确和精确的时间跨度度量的问题没有实际意义。

例如,您引用的是 3 天,而此查询给出的是 2 天。我在那里看不到任何错误。因为月份和年份彼此不同,所以您 无法 正确测量以月份为单位的时间 DISTANCE。这就像测量城市中的地理距离一样。

伦敦和巴黎之间有多少个纽约?厄尔布鲁士山有多少华沙高?你不可能有任何数学上正确的答案。

因此你只能用非精确的估计来回答。适用于互不相让的街头谈话。因此,任何基于 DateDiff 的查询本质上都会生成一个完全有效的答案,类似于“2Y 10M 给定或需要几天”——这个答案对于 "just give me overall impression".

的上下文是有效的

将这种感受它的简单性与数学准确性的完美主义结合起来是不可能的。例如,假设您得到大约 6Y 的跨度。现在你应该计算多少个闰年? 1999年至2004年的“6Y”中有两个闰年,而1998年至2003年的“6Y”中只有一个闰年。以下哪项是“6Y”的正确衡量标准???

然后我们有千禧年,其中 2000 年是闰年,但 1900 年不是。同样的 "sliding window" 问题会为您提供时间跨度(如“110Y”)中不稳定的未定义闰年数。如果你想在 "years and months" 中走向外行人的看法和计算时间跨度 - 你必须同意这让事情变得容易,简单和定义不精确。而几年中一天或几天的不匹配是正常的,可以