我所拥有的是一张包含所有名称及其相应日期的表格(我以“1900-08-22”的格式存储日期,但我们确实需要一个月和一天)
nameday 表:
name date
----------------
Bob 1900-04-22
Bob 1900-09-04
Frank 1900-01-02
...
诀窍在于每个名字可能有多个条目,并且有些人“nameday”总是在生日之后首次找到。所以说鲍勃#1出生于8月5日,他的名字日将在9月4日落下,但如果我们在9月4日之后出生,他的名字日将在4月22日。
显然,我的用户有一张桌子
用户表:
id first_name birth_date
------------------------------------
1 Bob 1975-08-05
2 Frank 1987-01-01
...
所以,eveything是基于生日,我们需要找到一个特定的人基于first_name和birthdate庆祝他/她的“nameday”。当我需要的是一个SQL服务器查询,它将能够为我的数据库中的人们提供名称日期;)
如果您认为将“nameday”表格中的日期拆分为:month&让我知道,我们可以做到这一点。我需要能够为此提出最有效的解决方案。
非常感谢任何帮助,
Wojo
答案 0 :(得分:2)
我建议您保留日期1904
- 因为1900
不是闰年且缺少Feb 29th
。
但是,这是您的数据查询:
WITH users AS
(
SELECT 1 AS id, 'Bob' AS first_name, CAST('1975-08-05' AS DATETIME) AS birth_date
UNION ALL
SELECT 2 AS id, 'Frank' AS first_name, CAST('1987-01-01' AS DATETIME) AS birth_date
),
namedays AS
(
SELECT 'Bob' AS name, CAST('1900-04-22' AS DATETIME) AS date
UNION ALL
SELECT 'Bob' AS name, CAST('1900-09-04' AS DATETIME) AS date
UNION ALL
SELECT 'Frank' AS name, CAST('1900-01-02' AS DATETIME) AS date
)
SELECT *
FROM users u
OUTER APPLY
(
SELECT COALESCE(
(
SELECT TOP 1 date
FROM namedays nd
WHERE nd.name = u.first_name
AND nd.date >= DATEADD(year, 1900 - YEAR(u.birth_date), birth_date)
ORDER BY
nd.date
),
(
SELECT TOP 1 date
FROM namedays nd
WHERE nd.name = u.first_name
ORDER BY
nd.date
)
) AS date
) dates
请在我的博客中查看此帖子,了解效果详情:
答案 1 :(得分:1)
你走了:
WITH "nameday_long" ("name", "date", "p_month_day") AS (
SELECT n."name",
n."date",
DATEPART(month, n."date") * 100 + DATEPART(day, n."date")
FROM "nameday" n
UNION ALL
SELECT n."name",
n."date",
DATEPART(month, n."date") * 100 + DATEPART(day, n."date") + 10000
FROM "nameday" n
)
SELECT u."id",
u."first_name",
u."birth_date",
n."date" AS nameday
FROM "user" u
LEFT JOIN "nameday_long" n
ON u."first_name" = n."name"
AND n."p_month_day" = (SELECT MIN(x."p_month_day") FROM "nameday_long" x WHERE x."p_month_day" > DATEPART(month, u."birth_date") * 100 + DATEPART(day, u."birth_date"))
诀窍是:
答案 2 :(得分:1)
这似乎是一个简单的解决方案:
select u.id, u.first_name, min(isnull(n.date, n2.date)) as nameday
from users u
left join namedays n on u.first_name = n.name and dateadd(year, year(u.birth_date) - 1900, n.date) > u.birth_date
left join namedays n2 on u.first_name = n2.name and dateadd(year, year(u.birth_date) - 1899, n2.date) > u.birth_date
group by u.id, u.first_name
说明:
使用users
表加入namedays
表(将namedays.date
列调整为与生日相同的年份)。如果调整后的日期是在他们的生日之后,则使用此日期。
如果没有,users
表再次与namedays
表联接,这次将date
列调整为生日后的年份,并使用之后的最小日期他们的生日。
此解决方案根据您的示例数据返回正确的日期,问题中列出的情况(Bob的生日更改为9/4之后返回Bob的名称日为4/22)。
答案 3 :(得分:0)
SELECT user.id, First_name, MIN(nameday.date)
FROM user
INNER JOIN nameday ON user.id = nameday.userid
WHERE user.birth_date < DateAdd(year, YEAR(u.birth_date) - year(nameday.date), nameday.date)
GROUP BY user.id, first_name
答案 4 :(得分:0)
SELECT Min(nameday。[date]),[user] .first_name
从名称日起
在nameday.name = [user] .first_name的INNER JOIN [user]
名称日期。[日期]&gt; = DateAdd(年, - (DatePart(yyyy,birth_date)-1900),birth_date)
GROUP BY [user] .first_name
答案 5 :(得分:0)
WITH namedays AS (
SELECT UPPER(nd.name),
nd.date,
MONTH(nd.date) 'month',
DAY(nd.date) 'day'
FROM NAMEDAYS nd)
birthdays AS (
SELECT u.id,
UPPER(u.first_name) 'first_name',
MONTH(u.birth_date) 'month',
DAY(u.birth_date) 'day'
FROM USERS u)
SELECT t.id,
t.first_name,
MIN(nd.date) 'name_day'
FROM USERS t
JOIN birthdays bd ON bd.id = t.id
JOIN namedays nd ON nd.month >= bd.month AND nd.day >= bd.day AND nd.name = bd.firstname
GROUP BY t.id, t.first_name
答案 6 :(得分:0)
DatePart(DayOfYear)是比较日期的好方法。而且谁知道我从SO那里学到了一些文化上的新东西? : - )
declare @nameday table
(
Name varchar(30),
Date smalldatetime
)
declare @user table
(
UserName varchar(30),
Birthday smalldatetime
)
insert into @nameday
select 'Bob', '1900-04-22' union
select 'Bob', '1900-09-04' union
select 'Frank', '1900-01-02'
insert into @user
select 'Bob', '1975-08-05' union
select 'Frank', '1987-01-01'
select UserName,
NameDayDate = Date
from @user as u
inner join @nameday as nd
on nd.Name = u.UserName
and datepart(dayofyear, nd.Date) >= datepart(dayofyear, u.Birthday)
and not exists (select *
from @nameday as ndOlder
where ndOlder.Name = nd.Name
and datepart(dayofyear, ndOlder.Date) >= datepart(dayofyear, u.Birthday)
and ndOlder.Date < nd.Date)