Postgres生日选择

Mik*_*ike 5 postgresql intervals date-of-birth

我使用Postgres数据库.此DB具有一个包含用户的表,该用户具有生日(日期字段).现在我想让所有在即将到来的一周里过生日的用户....

我的第一次尝试: SELECT id FROM public.users WHERE id IN (lange reeks) AND birthdate > NOW() AND birthdate < NOW() + interval '1 week'

但这并没有结果,显然是因为一年之后.我该如何解决这个问题?

有没有人知道PG会在29-02生日那天发生什么事情?

Mat*_*kel 9

我们可以使用postgres函数以非常好的方式执行此操作.

假设我们有一个表people,在列中有一个出生日期dob,这是一个日期,我们可以创建一个函数,允许我们将该列编入索引而忽略年份.(感谢ZoltánBöszörményi):

CREATE OR REPLACE FUNCTION indexable_month_day(date) RETURNS TEXT as $BODY$
  SELECT to_char($1, 'MM-DD');
$BODY$ language 'sql' IMMUTABLE STRICT;

CREATE INDEX person_birthday_idx ON people (indexable_month_day(dob));
Run Code Online (Sandbox Code Playgroud)

现在,我们需要查询表和索引.例如,要让每个人在任何一年的4月份过生日:

SELECT * FROM people 
WHERE 
    indexable_month_day(dob) >= '04-01'
AND 
    indexable_month_day(dob) < '05-01';
Run Code Online (Sandbox Code Playgroud)

有一个问题:如果我们的开始/结束时段超过一年的边界,我们需要更改查询:

SELECT * FROM people 
WHERE 
    indexable_month_day(dob) >= '12-29'
OR 
    indexable_month_day(dob) < '01-04';
Run Code Online (Sandbox Code Playgroud)

为了确保我们匹配闰日生日,我们需要知道我们是否会向前或向后"移动"它们.就我而言,两天都匹配起来比较简单,所以我的一般查询看起来像:

SELECT * FROM people 
WHERE 
    indexable_month_day(dob) > '%(start)%'
%(AND|OR)%
    indexable_month_day(dob) < '%(finish)%';
Run Code Online (Sandbox Code Playgroud)

我有一个django queryset方法,使这更简单:

def birthday_between(self, start, finish):
    """Return the members of this queryset whose birthdays
    lie on or between start and finish."""
    start = start - datetime.timedelta(1)
    finish = finish + datetime.timedelta(1)
    return self.extra(where=["indexable_month_day(dob) < '%(finish)s' %(andor)s indexable_month_day(dob) > %(start)s" % {
        'start': start.strftime('%m-%d'),
        'finish': finish.strftime('%m-%d'),
        'andor': 'and if start.year == finish.year else 'or'
    }]

def birthday_on(self, date):
    return self.birthday_between(date, date)
Run Code Online (Sandbox Code Playgroud)

现在,我可以做以下事情:

Person.objects.birthday_on(datetime.date.today())
Run Code Online (Sandbox Code Playgroud)

仅在前一天或仅在后一天匹配闰日生日也是可能的:您只需要将SQL测试更改为"> ="或"<=",而不是调整python中的开始/结束功能.


Dan*_*ons 4

我对此并不太有信心,但它似乎在我的测试中起作用。这里的关键是OVERLAPS运算符和一些日期算术。

我假设你有一张桌子:

create temporary table birthdays (name varchar, bday date);
Run Code Online (Sandbox Code Playgroud)

然后我往里面放了一些东西:

insert into birthdays (name, bday) values 
('Aug 24', '1981-08-24'), ('Aug 04', '1982-08-04'), ('Oct 10', '1980-10-10');
Run Code Online (Sandbox Code Playgroud)

这个查询将为我提供下周生日的人:

select * from 
  (select *, bday + date_trunc('year', age(bday)) + interval '1 year' as anniversary from birthdays) bd 
where 
  (current_date, current_date + interval '1 week') overlaps (anniversary, anniversary)
Run Code Online (Sandbox Code Playgroud)

date_trunc会截断年份中的日期,因此它应该可以让您了解当前年份。我最终不得不增加一年。这对我来说表明我出于某种原因有一个一对一的情况。也许我只需要找到一种方法来对日期进行四舍五入。无论如何,还有其他方法可以进行此计算。age给出从日期或时间戳到今天的间隔。我正在尝试添加生日和今天之间的年份以获得当年的日期。

真正的关键是用来overlaps查找日期重叠的记录。我使用周年纪念日两次来获取时间点。