Postgres 生日选择
我使用 Postgres 数据库。该数据库有一个包含用户的表,其中有出生日期(日期字段)。现在我想让所有在下周过生日的用户......
我的第一次尝试:SELECT id FROM public.users WHERE id IN (lange reeks) ANDbirthdate > NOW() 和出生日期 < NOW() + Interval '1 week'
但这并没有结果,显然是因为过年了。我该如何解决这个问题?
有谁知道 PG 在 29-02 生日时会发生什么情况?
I work with a Postgres database. This DB has a table with users, who have a birthdate (date field). Now I want to get all users who have their birthday in the upcoming week....
My first attempt: SELECT id FROM public.users WHERE id IN (lange reeks) AND birthdate > NOW() AND birthdate < NOW() + interval '1 week'
But this does not result, obviously because off the year. How can I work around this problem?
And does anyone know what happen to PG would go with the cases at 29-02 birthday?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
我们可以使用 postgres 函数以一种非常好的方式来做到这一点。
假设我们有一个表
people
,在dob
列中包含出生日期,这是一个日期,我们可以创建一个函数,允许我们索引该列而忽略那一年。 (感谢Zoltán Böszörményi):现在,我们需要查询针对表和索引。例如,要获取在任何一年的四月生日的每个人:
有一个问题:如果我们的开始/结束时间跨过一年边界,我们需要更改查询:
为了确保我们匹配闰日生日,我们需要知道我们是否会将它们向前或向后“移动”一天。就我而言,在这两天进行匹配更简单,因此我的一般查询如下所示:
我有一个 django 查询集方法,使这一切变得更加简单:
现在,我可以执行以下操作:
仅在前一天或仅后一天也是可能的:您只需将 SQL 测试更改为“>=”或“<=”,而不调整 python 函数中的开始/结束。
We can use a postgres function to do this in a really nice way.
Assuming we have a table
people
, with a date of birth in the columndob
, which is a date, we can create a function that will allow us to index this column ignoring the year. (Thanks to Zoltán Böszörményi):Now, we need to query against the table, and the index. For instance, to get everyone who has a birthday in April of any year:
There is one gotcha: if our start/finish period crosses over a year boundary, we need to change the query:
To make sure we match leap-day birthdays, we need to know if we will 'move' them a day forward or backwards. In my case, it was simpler to just match on both days, so my general query looks like:
I have a django queryset method that makes this all much simpler:
Now, I can do things like:
Matching leap-day birthdays only on the day before, or only the day after is also possible: you just need to change the SQL test to a `>=' or '<=', and not adjust the start/finish in the python function.
我对此并不太有信心,但它似乎在我的测试中起作用。这里的关键是 OVERLAPS 运算符和一些日期算术。
我假设你有一个表:
然后我在其中放入一些内容:
这个查询将为我提供下周生日的人:
date_trunc
截断当年的日期,所以它应该让你直到本年度。我最终不得不增加一年。这对我来说表明我出于某种原因有一个一对一的情况。也许我只需要找到一种方法来对日期进行四舍五入。无论如何,还有其他方法可以进行此计算。age
为您提供从日期或时间戳到今天的间隔。我正在尝试添加生日和今天之间的年份以获得当年的日期。真正的关键是使用重叠来查找日期重叠的记录。我使用周年纪念日两次来获取时间点。
I'm not overly confident in this, but it seems to work in my testing. The key here is the
OVERLAPS
operator, and some date arithmetic.I assume you have a table:
Then I put some stuff into it:
This query will give me the people with birthdays in the next week:
The
date_trunc
truncates the date at the year, so it should get you up to the current year. I wound up having to add one year. This suggests to me I have an off-by-one in there for some reason. Perhaps I just need to find a way to get dates to round up. In any case, there are other ways to do this calculation.age
gives you the interval from the date or timestamp to today. I'm trying to add the years between the birthday and today to get a date in the current year.The real key is using
overlaps
to find records whose dates overlap. I use the anniversary date twice to get a point-in-time.最后,为了显示接下来 14 天即将到来的生日,我使用了以下内容:
所以:
为了解决闰年问题,我将生日和当前日期都设置为2000年,
并仅处理从该初始正确日期开始的间隔。
为了处理临近结束/开始日期,
我首先将 2000 年当前日期与 2000 年生日间隔进行比较,
如果当前日期是年底,生日是年初,
我将 2001 年生日与 2000 年当前日期间隔进行了比较。
Finally, to show the upcoming birthdays of the next 14 days I used this:
So:
To solve the leap-year issue, I set both birthdate and current date to 2000,
and handle intervals only from this initial correct dates.
To take care of the near end/beginning dates,
I compared first the 2000 current date to the 2000 birthday interval,
and in case current date is at the end of the year, and the birthday is at the beginning,
I compared the 2001 birthday to the 2000 current date interval.
这是一个在大多数情况下都能得到正确结果的查询。
(它不能正确处理闰年;但您可以再次使用
extract
来根据闰年而不是当前年份来进行子选择。编辑:它适用于所有情况,并且作为一个有用的查询而不是标量选择,我使用了一些额外的子选择,这样我就不必为月份和日期输入相同的日期或表达式两次,当然还有实际的日期或表达式。数据将在表中而不是在
values
表达式。您可能仍会通过为包含闰日的周制作更智能的系列来对此进行改进,因为有时该间隔仅包含 6 天(对于非闰年)。 我将尝试从内到外解释这一点;我要做的第一件事是将目标日期(通常是 CURRENT_DATE,但在此代码中明确表示)标准化为我知道是闰年的年份。 ,因此 2 月 29 日出现在下一步是生成与所有正在考虑的月日对的关系;由于没有简单的方法来进行月日间隔检查,所以这一切都是使用
generate_series
进行的,从那里,从目标关系中提取月份和日期是一个简单的问题(
>people
别名)并仅过滤子选择中的行。正如我在这里所做的那样,按天进行操作应该会一直很好地工作,直到两个月的间隔(如果您想观察那么远),从 12 月 31 日起+两个月,变化应该包括闰日。另一方面,对于这样的查询,只处理整个月几乎肯定更有用,在这种情况下,除了
extract(month from ...
.Here's a query that gets the right result, most of the time.
(it doesn't take care of leap years correctly; but you could use
extract
again to work the subselect in terms of a leap year instead of the current year.EDIT: Got it working for all cases, and as a useful query rather than a scalar select. I'm using some extra subselects so that I don't have to type the same date or expression twice for month and day, and of course the actual data would be in a table instead of the
values
expression. You might adapt this differently. It might still stand to improve by making a more intelligent series for weeks containing leap days, since sometimes that interval will only contain 6 days (for non-leap years).I'll try to explain this from the inside-out; First thing I do is normalize the target date (
CURRENT_DATE
usually, but explicit in this code) into a year that I know is a leap year, so that February 29th appears among dates. The next step is to generate a relation with all of the month-day pairs that are under consideration; Since there's no easy way to do an interval check in terms of month-day, it's all happening usinggenerate_series
,From there it's a simple matter of extracting the month and day from the target relation (the
people
alias) and filtering just the rows that are in the subselect.Operating on days, as I've done here, should work splendidly all the way up until a two month interval (if you wanted to look out that far), since december 31 + two months and change should include the leap day. On the other hand, it's almost certainly more useful to just work on whole months for such a query, in which case you don't really need anything more than
extract(month from ...
.首先使用
age()
找出此人当前的年龄,然后从extract(year from Age())
中获取年份。这是他们当前的年龄(以岁为单位),因此他们下一次生日时的年龄在年份上加 1。然后,通过在他们的生日上添加这么多年的间隔*间隔'1年'
来找到他们的下一个生日。完毕。我在这里使用了子选择将
next_birth_day
列添加到完整的表中,以使 select 子句更简单。然后,您可以使用 where 条件来满足您的需求。First find out how old the person currently is using
age()
, then grab the year from thatextract(year from age())
. This is how old they are currently in years, so for their age at their next birthday add 1 to the year. Then their next birthday is found by adding an interval of this many years* interval '1 year'
to their birthday. Done.I've used a subselect here to add the
next_birth_day
column in to the complete table to make the select clause simpler. You can then play with the where conditions to suit your needs.这是基于 Daniel Lyons 的周年纪念想法,通过计算下一个生日和今天之间的间隔,只需使用 +/- 日期算术:
This is based on Daniel Lyons's anniversary idea, by calculating the interval between the next birthday and today, with just +/- date arithmetic:
如果您希望它与闰年一起使用:
然后:
另请参阅:
获取 PostgreSQL 中今天生日的所有条目
In case you want it to work with leap years:
Then:
See also:
Getting all entries who's Birthday is today in PostgreSQL
示例:生日介于 1 月 20 日和 2 月 10 日之间
为什么是 1800?
无论是任何一年;
在我的注册表中,我可以告知出生日期(带年份)或仅告知生日(不带年份),在这种情况下,我保存为 1800,以便更轻松地处理日期
Exemple: birthdate between: jan 20 and feb 10
Why 1800?
No matter may be any year;
In my registration form, I can inform the date of birth (with years) or just the birthday (without year), in which case I saved as 1800 to make it easier to work with the date
这是我的看法,也适用于闰年:
Here's my take, which works with leap years too:
我知道这篇文章很旧,但我遇到了同样的问题,并提出了这个简单而优雅的解决方案:
对于在过去 20 天内过生日的人来说,使用age() 并计算圈年非常简单:
I know this post is old, but I had the same issue and came up with this simple and elegant solution:
It is pretty easy with age() and accounts for lap years... for the people who had their birthdays in the last 20 days:
我只是根据原始出生日期创建了今年的日期。
我希望这有帮助。
I have simply created this year date from original birth date.
I hope this help.