【问题标题】:Count specific date in date range in SQL在 SQL 中计算日期范围内的特定日期
【发布时间】:2017-01-09 12:24:49
【问题描述】:

我想问您如何从 Oracle SQL 的日期范围 (DD-MON-YY) 中获取特定日期 (DD-MON) 的计数? 这就是我所拥有的:

MEMBERS    INSTALL_D   DOB      SYSDATE
6934109    22-FEB-12   18-NOV   09-JAN-17
6934109    22-FEB-12   13-JUN   09-JAN-17
6934109    22-FEB-12   12-AUG   09-JAN-17
6934109    22-FEB-12   24-OCT   09-JAN-17
6934109    22-FEB-12   04-FEB   09-JAN-17

我想从日期 INSTALL_D 和 SYSDATE 的范围内计算 DOB。比方说

  1. 18-NOV 行出现 5 次,
  2. 13-JUN 的行出现了 5 次,
  3. 12-AUG 行出现 5 次,
  4. 24-OCT 的行出现 5 次

最后一个只有4次。

所以,结果应该是这样的:

MEMBERS   INSTALL_D   DOB      SYSDATE    COUNT(DOB)
6934109    22-FEB-12   18-NOV   09-JAN-17  5
6934109    22-FEB-12   13-JUN   09-JAN-17  5
6934109    22-FEB-12   12-AUG   09-JAN-17  5
6934109    22-FEB-12   24-OCT   09-JAN-17  5
6934109    22-FEB-12   04-FEB   09-JAN-17  4

我尝试使用 MONTHS_BETWEEN 函数,但这不是我想要的。

编辑:
我会更具体。我使用这个查询来获取上面的第一个表:
select B0022HOME_NO as Members, B0022INSTALLDATE as INSTALL_DATE, to_char(to_date (encryption.decrypt(a.B0031MEMDATEOFBIRT),'DD-MON-YYYY'),'DD-MON')as DOB, SYSDATE from BT0022, BT0031 a where B0022HOME_NO=a.B003101HOME_NO and BT0022.B0022HOME_NO=6934109;

INSTALL_D 是安装日期,在这个例子中是 22-FEB-12
DOB 是会员的生日。只有日期和月份 (DD-MON)
SYSDATE 是实际日期。

假设 sysdate 是 18-NOV-12 并且 INSTALL_D 是 22-FEB-12 并且成员的 DOB 是 18-NOV,这意味着 DOB 在这个日期范围内出现一次。
例如:INSTALL_D 22-FEB-12 and SYSDATE 09-JAN-17 is DOB of 18-NOV 出现 5 次,但如果 SYSDATE 为 18-NOV-17,则为 6 次。这就是我问你关于日期范围内的 count(DOB) 的问题。

提前致谢

【问题讨论】:

  • 列数据类型?您使用的是哪个 dbms?
  • INSTALL_D 列是 DATE 类型,DOB 是 varchar2。 DBMS 是 Oracle 数据库 10g 版本 10.2.0.1.0 - 64 位
  • 此设置存在严重问题。为什么“SYSDATE”是该表中的一列? SYSDATE 是一个系统函数,它的值是动态计算的,它不存储在表中。您是要使用 SYSDATE(但表中没有相应的列),还是要使用不应称为“SYSDATE”的存储日期?然后-您是不是要更改 MEMBERS 列,以便在所有行中都没有相同的值? (我不认为同一个成员有不同的出生日期)。另外,为什么 DOB 存储为 VARCHAR2,而不是日期(包括年份)?
  • SYSDATE 不必在那里。我的意思是第一个选项:表中没有列,只使用 SYSDATE。我不知道为什么 DOB 存储为 VARCHAR2。我开始使用这种类型的系统。我无法改变它。在这个特定的例子中,我不需要知道 DOB 的年份。

标签: sql oracle count date-range


【解决方案1】:

这是另一种有望适用于闰年的出生日期的方法:

(注意,我假设您的所有日期都存储为 DATE,这意味着您的 dob 列有年份。如果不是这种情况,那么 a)您为什么不将年份存储在 dob 中? b)您必须更新我的查询以将字符串转换为适合比较的格式)

WITH sample_data AS (SELECT 6934109 members, to_date('22/02/2012', 'dd/mm/yyyy') install_d, to_date('18/11/1969', 'dd/mm/yyyy') dob FROM dual UNION ALL
                     SELECT 6934109 members, to_date('22/02/2012', 'dd/mm/yyyy') install_d, to_date('13/06/1991', 'dd/mm/yyyy') dob FROM dual UNION ALL
                     SELECT 6934109 members, to_date('22/02/2012', 'dd/mm/yyyy') install_d, to_date('12/08/1982', 'dd/mm/yyyy') dob FROM dual UNION ALL
                     SELECT 6934109 members, to_date('22/02/2012', 'dd/mm/yyyy') install_d, to_date('24/10/1963', 'dd/mm/yyyy') dob FROM dual UNION ALL
                     SELECT 6934109 members, to_date('22/02/2012', 'dd/mm/yyyy') install_d, to_date('04/02/1975', 'dd/mm/yyyy') dob FROM dual UNION ALL
                     SELECT 6934110 members, to_date('28/02/2011', 'dd/mm/yyyy') install_d, to_date('29/02/1976', 'dd/mm/yyyy') dob FROM dual UNION ALL
                     SELECT 6934110 members, to_date('29/02/2012', 'dd/mm/yyyy') install_d, to_date('01/03/1975', 'dd/mm/yyyy') dob FROM dual UNION ALL
                     SELECT 6934111 members, to_date('01/01/2017', 'dd/mm/yyyy') install_d, to_date('12/01/1957', 'dd/mm/yyyy') dob FROM dual UNION ALL
                     SELECT 6934111 members, to_date('01/01/2017', 'dd/mm/yyyy') install_d, to_date('02/01/1980', 'dd/mm/yyyy') dob FROM dual UNION ALL
                     SELECT 6934112 members, to_date('01/03/2011', 'dd/mm/yyyy') install_d, to_date('29/02/1976', 'dd/mm/yyyy') dob FROM dual UNION ALL
                     SELECT 6934112 members, to_date('29/02/2012', 'dd/mm/yyyy') install_d, to_date('29/02/1976', 'dd/mm/yyyy') dob FROM dual)
SELECT members,
       install_d,
       dob,
       SYSDATE,
       date_of_first_year,
       date_of_latest_year,
       months_between(date_of_latest_year, date_of_first_year)/12 + 1 count_birthdays
FROM   (SELECT members,
               install_d,
               dob,
               SYSDATE,
               CASE WHEN to_char(dob, 'mmdd') < to_char(install_d, 'mmdd') THEN
                         trunc(add_months(install_d, 12), 'yyyy')
                    ELSE trunc(install_d, 'yyyy')
               END date_of_first_year,
               CASE WHEN to_char(dob, 'mmdd') <= to_char(SYSDATE, 'mmdd') THEN
                         trunc(SYSDATE, 'yyyy')
                    ELSE add_months(trunc(SYSDATE, 'yyyy'), -12)
               END date_of_latest_year
        FROM   sample_data);

   MEMBERS INSTALL_D   DOB         SYSDATE     DATE_OF_FIRST_YEAR DATE_OF_LATEST_YEAR COUNT_BIRTHDAYS
---------- ----------- ----------- ----------- ------------------ ------------------- ---------------
   6934109 22/02/2012  18/11/1969  09/01/2017  01/01/2012         01/01/2016                        5
   6934109 22/02/2012  13/06/1991  09/01/2017  01/01/2012         01/01/2016                        5
   6934109 22/02/2012  12/08/1982  09/01/2017  01/01/2012         01/01/2016                        5
   6934109 22/02/2012  24/10/1963  09/01/2017  01/01/2012         01/01/2016                        5
   6934109 22/02/2012  04/02/1975  09/01/2017  01/01/2013         01/01/2016                        4
   6934110 28/02/2011  29/02/1976  09/01/2017  01/01/2011         01/01/2016                        6
   6934110 29/02/2012  01/03/1975  09/01/2017  01/01/2012         01/01/2016                        5
   6934111 01/01/2017  12/01/1957  09/01/2017  01/01/2017         01/01/2016                        0
   6934111 01/01/2017  02/01/1980  09/01/2017  01/01/2017         01/01/2017                        1
   6934112 01/03/2011  29/02/1976  09/01/2017  01/01/2012         01/01/2016                        5
   6934112 29/02/2012  29/02/1976  09/01/2017  01/01/2012         01/01/2016                        5

如果您希望 2 月 29 日的出生日期与非闰年的 3 月 1 日相匹配,唯一美中不足的是。

我上面的查询没有那个逻辑,但你总是可以添加一个检查来比较 install_d 和 sysdate 年 2 月的最后一天,如果它是 28 日,则将出生日期更改为 '29/ 02' 到 '01/03' 在进行比较之前。 (即将 29/02/2012 的 install_d 与 dob = 29/02 进行比较,因为 2012 年是闰年。您不需要比较 sysdate,因为 0301 >= 0229 和 0228

【讨论】:

  • 嗯...有趣的话题我以前没有想过。 “飞跃者”的出生日期具有法律意义(例如,“飞跃者”何时可以获得驾驶执照?)我一直认为在非闰年是 2 月 28 日,但维基百科显示在许多司法管辖区是 3 月 1 日。这对我来说没有多大意义;闰日是 2 月 29 日,因为它是在日历年仍从 3 月 1 日开始的时候发明的。(9 月是第 7 个月,10 月是 8 日等等)所以 2 月 29 日也是一年的最后一天,而不仅仅是月。如果不存在,为什么要将周年纪念日移到不同的年份?
  • @mathguy 我总是假设 3 月 1 日,因为:(2 月的最后一天)+ 366 天 = 2 月 29 日(闰年)或 3 月 1 日(非闰年)
  • 例如,add_months() 不使用该逻辑。它匹配月末到月末,无论是 30 还是 31。由于我们必须“提前一天”,如果至少一个月(以及凯撒大帝之前的日历年)对我来说更有意义保留,而不是将日期移至下个月和“年”。但是,当然,对我有意义的是无关紧要的。如果 OP 将其用于个人娱乐以外的任何用途,他应该澄清业务(可能是法律)要求是什么。无论哪种方式都很有趣!
  • @Boneist:这就是我要找的。是的,闰年的问题。我有一个查询可以解决这个问题:
  • case when to_char(to_date (encryption.decrypt(B0031MEMDATEOFBIRT),'DD-MM-YYYY'),'DD.MM') like '29.02%' then floor(abs(MONTHS_BETWEEN(to_date(Sysdate,'DD.MM.YY'),to_date(to_char(to_date (encryption.decrypt(a.B0031MEMDATEOFBIRT),'DD-MON-YYYY')- interval '1' day,'DD.MM')||'.'||to_char(to_date (B0022INSTALLDATE,'DD-MM-YY'),'YY'),'DD.MM.YY'))/12))*20 else floor(abs(MONTHS_BETWEEN(to_date(Sysdate,'DD.MM.YY'),to_date(to_char(to_date (encryption.decrypt(a.B0031MEMDATEOFBIRT),'DD-MON-YYYY'),'DD.MM')||'.'||to_char(to_date (B0022INSTALLDATE,'DD-MM-YY'),'YY'),'DD.MM.YY'))/12))*20 end
【解决方案2】:

您可以按日期段分组,这不是最漂亮的东西,但会起作用。例如

GROUP BY CAST(DATEPART(mm, dob) AS VARCHAR(2)) + '-' + CAST(DATEPART(yyyy, dob) AS VARCHAR(4))

你也可以把它放在 select 语句中

【讨论】:

    【解决方案3】:

    试试这个:(分组应该可以)

    SELECT MEMBERS,INSTALL_D,DOB,SYSDATE,Count(DOB) FROM table_name GROUP BY  DOB ,MEMBERS,INSTALL_D  ,SYSDATE
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-18
      • 2017-06-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多