使用Oracle SQL选择具有给定范围内生日的员工

时间:2012-03-25 15:51:29

标签: sql database oracle datetime date-arithmetic

为了选择两个月之间的生日,其中FROMDATE和TODATE是预备语句中的一些参数,我想像这样:

select
  p.id as person_id,
   ...
   ...
  where e.active = 1
        and extract(month from TODATE) >= extract(month from e.dateOfBirth)
        and extract(month from e.dateOfBirth) >= extract(month from FROMDATE)
        order by extract(month from e.dateOfBirth) DESC,
              extract(day from e.dateOfBirth) DESC

如何改善这项工作以适应日子?

8 个答案:

答案 0 :(得分:2)

在Oracle中搜索日期范围的方法不止一种。对于您的场景,我建议您将所涉及的所有日期的月和日元素转换为数字。

select
  p.id as person_id,
   ...
   ...
  where e.active = 1
  and to_number (to_char( e.dateOfBirth, 'MMDD')) 
      between to_number (to_char( FROMDATE, 'MMDD'))
              and to_number (to_char( TODATE, 'MMDD')) 
  order by extract(month from e.dateOfBirth) DESC,
          extract(day from e.dateOfBirth) DESC

这不会使用e.dateOfBirth列上的任何索引。这是否重要取决于您想要运行查询的频率。


@AdeelAnsari评论:

  

“我不喜欢to_char谓词,以便利用   索引“

什么指数? dateOfBirth上的正常索引不会有任何用处,因为索引条目将以year元素引导。因此,它无法帮助您找到12月23日任何出生的人的所有记录。

基于函数的索引 - 或者在11g中,带有索引的虚拟列(基本上是相同的东西) - 是索引日期列部分的唯一方法。

答案 1 :(得分:1)

你应该可以使用

SELECT * FROM mytbale
where dateofbirth between start_dt and end_dt

替代:

您可以使用以下日期将日期转换为日期:

to_char( thedate, 'ddd' )

然后检查范围(请注意,这与@Dems回答的问题相同,在12月25日到1月10日之间不应该跨越年底。)

答案 2 :(得分:1)

您是否需要最高性能,因此愿意更改架构?或者记录的数量如此之小,性能相对不重要,您希望查询能够按原样处理数据吗?

最简单,最快捷的方法是存储第二个生育数据字段,但是“没有”年份。我把引号括在'没有',因为日期实际上不能有一年。所以,相反,你只需将它建立在另一年。

根据我的经验,将每个DoB重新安排到2000年是一个不错的选择。因为它包括一个闰年,并且是一个很好的数字。每个DoB和FromDate以及ToDate都将在2000年开始工作......

WHERE
      DoB2000 >= FromDate
  AND DoB2000 <= ToDate

(这假设您还索引新字段以快速进行搜索,否则您仍然可以进行扫描,尽管它可能比以下替代方案更快。)


或者,您可以继续使用EXTRACT模式。但那会产生不幸的后果;它非常混乱,你永远不会得到索引搜索,你将永远得到一个索引扫描。这是因为搜索字段包含在函数调用中。

WHERE
  (   EXTRACT(month FROM e.DateOfBirth) > EXTRACT(month FROM FromDate)
      OR  (      EXTRACT(month FROM e.DateOfBirth)  = EXTRACT(month FROM FromDate)
             AND EXTRACT(day   FROM e.DateOfBirth) >= EXTRACT(day   FROM FromDate)
          )
  )
  AND
  (   EXTRACT(month FROM e.DateOfBirth) < EXTRACT(month FROM ToDate)
      OR  (      EXTRACT(month FROM e.DateOfBirth)  = EXTRACT(month FROM ToDate)
             AND EXTRACT(day   FROM e.DateOfBirth) <= EXTRACT(day   FROM ToDate)
          )
  )

答案 3 :(得分:1)

这是我在接下来的20天内用于birtdates的查询:

SELECT A.BIRTHDATE, 
    CEIL(ABS (MONTHS_BETWEEN(A.BIRTHDATE, SYSDATE) / 12)) AGE_NOW,  
    CEIL(ABS (MONTHS_BETWEEN(A.BIRTHDATE, SYSDATE + 20) / 12)) AGE_IN_20_DAYS
FROM USERS A
WHERE 
    CEIL(ABS (MONTHS_BETWEEN(A.BIRTHDATE, SYSDATE) / 12)) <> CEIL(ABS (MONTHS_BETWEEN(A.BIRTHDATE, SYSDATE + 20) / 12));
  • ABS (MONTHS_BETWEEN(A.BIRTHDATE, SYSDATE) / 12))以38.9,27.2等格式返回年龄
  • 应用ceil()会给我们多年的差异,我们需要确定这个人是否接近生日。例如

    1. 年龄:29.9
    2. 29.9 +(20天)= 30.2
    3. ceil(30.1) - ceil(29.9)= 1

这是查询december 16

的结果
BIRTHDATE    AGE_NOW    AGE_IN_20_DAYS

12/29/1981    35           36
12/29/1967    49           50
1/3/1973      44           45
1/4/1968      49           50

答案 4 :(得分:0)

我不知道这在性能方面表现如何,但我会尝试使用常规日期减法。比如说,您想要1月1日到7月1日之间的分娩日。 25到25.5之间的任何人都有资格。也就是说,任何部分年龄不到1/2岁的人都符合资格。数学很容易在1/2,但无论窗口如何都是一样的。换句话说,“一个月内”意味着一年的+/- 1/12,一天内意味着一年+/- 1/365,依此类推。

年份与此方法无关,因此我将在创建变量时使用当前年份。

另外,我会想到范围的中心,然后做绝对值(要么是,要么总是使用未来的日期)。

例如

select personid
from mytable
where abs(mod(dob - target_birth_date)) < 1/52

会在一周内给大家一个生日。

我意识到这段代码几乎不是伪代码,但似乎它可能允许你这样做,如果你稍微调整它,你仍然可以使用索引。只是试着跳出框框思考。

答案 5 :(得分:0)

最后,我们选择了不同的解决方案,我们添加拳头创建周年纪念日期:

where 
...
and (to_char(sysdate,'yyyy') - to_char(e.dateofbirth,'yyyy')) > 0
and add_months(e.dateofbirth,
              (to_char(sysdate,'yyyy') - to_char(e.dateofbirth,'yyyy')) * 12)
              >:fromDate:
and :toDate: > add_months(e.dateofbirth,
              (to_char(sysdate,'yyyy') - to_char(e.dateofbirth,'yyyy')) * 12)
order by extract(month from e.dateofbirth) DESC,
              extract(day from e.dateofbirth) DESC)

答案 6 :(得分:0)

这就是解决方案!!!

SELECT                                  
    *                           
FROM                                    
    PROFILE  prof                          
where                        
      to_date(to_char(prof.BIRTHDATE, 'DDMM'), 'DDMM') BETWEEN sysdate AND sysdate+5

add_months(to_date(to_char(prof.BIRTHDATE, 'DDMM'), 'DDMM'),12) BETWEEN sysdate AND sysdate+5  

答案 7 :(得分:-2)

伙计我有一个更简单的解决方案来解决这个问题

  • 步骤1.将月份转换为数字,
  • 步骤2.将日期(两位数)连接到月份
  • 步骤3.然后通过执行to_number(结果)
  • 将结果转换为数字
  • 步骤4.一旦你有了开始日期和结束日期,然后在它上面执行一个功能,你就完成了。

示例代码:

SELECT      date_of_birth            
       FROM xxxtable
       where to_number(to_number(to_char(to_date(substr(date_of_birth,4,3),'mon'),'mm'))||substr(date_of_birth,1,2)) between
        to_number(to_number(to_char(to_date(substr(:start_date_variable,4,3),'mon'),'mm'))||(substr(:start_date_variable,1,2)))  and  
        to_number(to_number(to_char(to_date(substr(:end_date_variable,4,3),'mon'),'mm'))||(substr(:end_date_variable,1,2)));