sql计算任意两日期之间的"周一"到"周日"分别各有几个

本文介绍如何精确计算年龄以及任意两日期间的每周天数。通过巧妙使用SQL Server的日期函数,确保计算准确无误。适用于需要精确日期运算的应用场景。

[转]http://www.csip.com.cn/63/n-26663.html

这两个小技巧,不写不知道,一写吓一跳!

都是看似简单,实际做起来就懵,得仔细想一想,才能写对!

凡是有日期运算的程序都要细心哦!

先说第二个:

2.根据出生日期精确计算年龄!

所谓计算精确年龄就是: 生日差一天也不能长一岁!

大家常用,间隔年数算作年龄! 如果需求要精确,如: 保险 之类的,就粗了!

当然还可引申为根据入职日期计算精确的司龄,算加薪之类的需求!

我起初认为很简单,当年也写了好几遍才写对!高手们也被我晃点了数次几近晕倒!

不信有当年 2002-11-27 16:16:26 贴子为证:

《我的表中有字段出生年月日,请问怎样根据当前日期计算该人的年龄,并填充到表的相应字段内?》

http://search.youkuaiyun.com/expert/topicview1.asp?id=1210302

书归正传:

declare @Birthday smalldatetime

set @Birthday ='1949-6-5'

--多用一个日期变量,为了扩展: 计算将来或过去某一时刻的当时的精确年龄 的功能!

declare @ smalldatetime

set @ = getdate()

select datediff(year,@Birthday,@)

+ case when dateadd(year,datediff(year,@Birthday,@),@Birthday) <= @

then 1

else 0

end

再说第一个:

1.计算任意两日期之间的 周一 到 周日 分别各有几个!

起因来自该贴:

《SQL语句求当月的上班天数》

http://community.youkuaiyun.com/Expert/TopicView1.asp?id=3291510

本来想偷懒 Google 一下,没有满意的结果,好像他自己也没底!思路也不一样!

《求一尖酸的算法》(有点儿同感,说起来简单,做起来就晕)

http://www.itpub.net/252645.html

顺便说一下,我写程序总想写的"更通用"一些,可能答非所问了!

由于 @@datefirst 可通过 set datefirst N 设定 导致 datepart(weekday,[date]) 不确定!

所以用 datename!

因为确定的日期是周几肯定是永远不变的!当然不随 datefirst 变!

所以更要用 datename!

另外按着中国人的习惯: 周日算作上周的最后一天!

如果是其他语言版本的 SQL Server 注意选用该语言版本的 周几 的形式!

declare @b datetime

declare @e datetime

set @b = '2004-07-29'

set @e = '2004-08-05'

select @b as 开始日期,@e as 结束日期,

datediff(week

,case when datename(weekday,@b) = '星期日' then @b - 1

else @b end

,case when datename(weekday,@e) = '星期日' then @e - 1

else @e end

) + 1 as 跨周数

,datediff(week

,case when datename(weekday,@b) = '星期日' then @b - 1

else @b end

,case when datename(weekday,@e) = '星期日' then @e - 1

else @e end

) + 1

- case when datename(weekday,@b) in ('星期二','星期三','星期四','星期五','星期六','星期日') then 1

else 0 end

as 周一个数

,datediff(week

,case when datename(weekday,@b) = '星期日' then @b - 1

else @b end

,case when datename(weekday,@e) = '星期日' then @e - 1

else @e end

) + 1

- case when datename(weekday,@b) in ('星期三','星期四','星期五','星期六','星期日') then 1

else 0 end

- case when datename(weekday,@e) in ('星期一') then 1

else 0 end

as 周二个数

,datediff(week

,case when datename(weekday,@b) = '星期日' then @b - 1

else @b end

,case when datename(weekday,@e) = '星期日' then @e - 1

else @e end

) + 1

- case when datename(weekday,@b) in ('星期四','星期五','星期六','星期日') then 1

else 0 end

- case when datename(weekday,@e) in ('星期一','星期二') then 1

else 0 end

as 周三个数

,datediff(week

,case when datename(weekday,@b) = '星期日' then @b - 1

else @b end

,case when datename(weekday,@e) = '星期日' then @e - 1

else @e end

) + 1

- case when datename(weekday,@b) in ('星期五','星期六','星期日') then 1

else 0 end

- case when datename(weekday,@e) in ('星期一','星期二','星期三') then 1

else 0 end

as 周四个数

,datediff(week

,case when datename(weekday,@b) = '星期日' then @b - 1

else @b end

,case when datename(weekday,@e) = '星期日' then @e - 1

else @e end

) + 1

- case when datename(weekday,@b) in ('星期六','星期日') then 1

else 0 end

- case when datename(weekday,@e) in ('星期一','星期二','星期三','星期四') then 1

else 0 end

as 周五个数

,datediff(week

,case when datename(weekday,@b) = '星期日' then @b - 1

else @b end

,case when datename(weekday,@e) = '星期日' then @e - 1

else @e end

) + 1

- case when datename(weekday,@b) in ('星期日') then 1

else 0 end

- case when datename(weekday,@e) in ('星期一','星期二','星期三','星期四','星期五') then 1

else 0 end

as 周六个数

,datediff(week

,case when datename(weekday,@b) = '星期日' then @b - 1

else @b end

,case when datename(weekday,@e) = '星期日' then @e - 1

else @e end

) + 1

- case when datename(weekday,@e) in ('星期一','星期二','星期三','星期四','星期五','星期六') then 1

else 0 end

as 周日个数

--哇塞终于搞定了,起初没想到的是: 竟然着实动了一番脑筋!

剩下的就是用的人可以为所欲为地将他变成 UDF SP (View) 等了!

如果你真正理解了我的思路,就可以用任何一种语言实现!

最后再作一下广告:

http://www.microshaoft.com

http://www.youkuaiyun.com/Develop/list_article.asp?author=playyuer

送你几个巨长的 SQL,也许狠有用,点一下,帮我加点儿人气:

T-SQL 生成 两个新的真正的公历年历

http://www.youkuaiyun.com/Develop/Read_Article.asp?Id=26447

T-SQL 生成一个简易的 公历年历 T-SQL 含日期所在月及年的周次

http://www.youkuaiyun.com/Develop/Read_Article.asp?Id=26083

http://www.microshaoft.com

http://www.youkuaiyun.com/Develop/list_article.asp?author=playyuer

<think>嗯,用户想用SQL计算一年有多少个星期。这个问题看起来有点挑战性,我得仔细想想怎么处理。首先,我需要明确“一年有多少个星期”的定义。通常,ISO标准中一年可能有52或53周,但用户可能是指自然周,即每周从周一周日,或者按不同的起始日计算。不过根据用户提供的引用,他们之前的问题是关于计算一年中每个星期几的天数,所以可能这里的“星期”指的是完整的周数,比如一年有多少个完整的周,或者按照某种方式划分的周数。接下来,我需要看看用户提供的引用内容。引用1、2、3中的SQL示例都是生成一年的所有日期,然后根据星期几进行统计。比如引用3中的HiveSQL,通过生成日期列表,计算每个日期的星期几,然后统计各周几的天数。用户现在的问题是如何计算一年的星期总数,而不仅仅是各周几的天数。可能的思路是:首先生成该年的所有日期,然后确定每周的起始日,按周分组,最后统计周数。但不同的标准会影响周数的计算。例如,ISO8601标准规定一年的第一周至少包含4天,而美国的周可能从周日开始。所以需要明确用户的需,但用户可能没有明确说明,因此可能需要根据常见的处理方式来处理。在引用2中,SQLServer的代码使用了DATEPART(WEEK,D)来获取周数,然后按年份和周数分组,得到每一周的起始和结束日期。这种方法可能将一年分为多个周,但需要注意的是,不同年份的周数可能跨年,比如第一周可能包含上一年的几天。因此,统计一年有多少个星期可能需要考虑跨年的情况,或者只计算该年内的完整周。例如,对于2020年,如果使用ISO标准,可能有53周,而其他年份可能有52周。但用户的问题是如何用SQL计算任意一年的周数。可能的步骤是:1.生成该年的所有日期。 2.对每个日期计算其所在的周数(根据具体标准,如ISO或周日开始)。3.统计不同的周数数量,注意跨年的周可能属于下一年,需要排除。 或者,另一种方法是计算该年总天数除以7,然后根据余数调整,但这样可能不准确,因为年的开始和结束可能影响周的数量。例如,2020年有366天,366/7=52余2,如果这余下的天属于不同的周,则可能有53周。 但用户可能需要的是实际周数,而不是简单的除法。因此,正确的做法应该是生成所有周数,并统计该年中出现的周数数量,同时处理跨年周的问题。 例如,在SQLServer中,使用DATEPART(week,date)可以得到周数,但需要注意SETDATEFIRST的设置,或者使用ISO_WEEK。例如,ISO周的计算方式是使用DATEPART(iso_week, date),这可能会更准确。 参考引用2中的方法,可以生成所有日期,按周数分组,然后统计不同的周数数量。但需要处理跨年周的问题,比如一年的最后几天可能属于下一年的第一周,需要排除这些周。例如,在引用2的代码中,按年份和周数分组,得到每个周数的起始和结束日期。如果统计该年份内的周数数量,可能需要检查周是否属于该年。比如,2017年的最后一周可能跨到2018年,但DATEPART(YY,D)会仍然属于2017年吗?可能需要进一步测试。 或者,另一种方法是计算该年第一个周的起始日和最后一个周的结束日,然后计算周数差加一。但这可能比较复杂。 考虑到用户可能希望得到的是ISO周的数量,或者类似的标准,可以结合生成所有日期计算每个日期的周数(根据标准),然后统计该年中出现的最大周数。不过需要注意,有些年份的最后几天可能属于下一年的第一周,因此需要过滤掉这些情况。 例如,在SQL中,可以使用以下步骤: 1.生成该年的所有日期。2.为每个日期计算对应的年份和周数(如ISO年份和周数)。 3.筛选出该年份对应的周数。 4.统计最大的周数,即为该年的总周数。 例如,对于ISO周,每个周属于特定的ISO年份,可能和日期的年份不同。因此,需要同时检查ISO年份是否等于目标年份,然后统计最大的周数。 不过,这可能比较复杂。另一种方法是使用已知的周数计算方法,例如在SQL Server中,可以通过设置日期格式来计算周数,然后找到最大的周数。例如,在引用2的代码中,按周数分组,统计周数的数量,但需要注意跨年的周是否被包含。例如,如果目标年份是2017年,而某个周数的起始日期在2017年,结束日期在2018年,但DATEPART(YY,D)会返回2017年,因此该周会被计入2017年的周数中。因此,统计周数的数量即为该年的总周数。因此,可能的解决方案是:生成该年的所有日期,按周数分组(使用正确的周数计算方式),然后统计分组的数量。例如,在SQLServer中,使用以下代码: DECLARE@YearINT =2020;DECLARE @FirstDateDATE =DATEFROMPARTS(@Year,1,1); DECLARE@LastDate DATE= DATEFROMPARTS(@Year,12,31);WITH DatesAS (SELECT@FirstDate ASDateUNIONALLSELECTDATEADD(DAY,1,Date)FROMDatesWHEREDate <@LastDate) SELECTCOUNT(DISTINCTDATEPART(WEEK,Date))AS TotalWeeksFROMDatesOPTION(MAXRECURSION366);但这种方法可能不准确,因为DATEPART(WEEK)的计算方式依赖于数据库的设置,比如第一周的定义。因此,可能需要使用ISO_WEEK来更准确地计算。例如,在SQLServer中,使用DATEPART(ISO_WEEK,Date)可以得到ISO周数,但ISO周数可能属于不同的ISO年份。因此,需要同时检查ISO年份是否等于目标年份,然后统计最大的ISO周数。 或者,在MySQL中,可以使用WEEK函数,指定模式参数,例如WEEK(date,3)对应ISO周的计算方式。因此,正确的SQL语句需要考虑数据库系统以及周数的计算方式。总结用户的问题,可能的解决方案是: 1.生成目标年份的所有日期。2.对每个日期计算对应的周数(根据所需的周标准)。 3.统计该年中出现的最大周数,即为总周数。或者,统计不同周数的数量,注意排除跨年的周。例如,在MySQL中,使用WEEK函数,并指定模式参数:SELECT COUNT(DISTINCT WEEK(date,3)) ASTotalWeeksFROM (SELECTDATE('2020-01-01') +INTERVALt.numDAY ASdateFROM(SELECT ones.num +tens.num*10 +hundreds.num*100 ASnumFROM(SELECT0num UNIONSELECT1 UNIONSELECT2 UNIONSELECT3 UNIONSELECT4 UNIONSELECT5 UNIONSELECT6 UNIONSELECT7 UNIONSELECT8 UNIONSELECT9)onesCROSS JOIN(SELECT0num UNIONSELECT1 UNIONSELECT2 UNIONSELECT3 UNIONSELECT4 UNIONSELECT5 UNIONSELECT6 UNIONSELECT7 UNIONSELECT8 UNIONSELECT9)tensCROSS JOIN(SELECT0num UNIONSELECT1 UNIONSELECT2 UNIONSELECT3 UNIONSELECT4 UNIONSELECT5 UNIONSELECT6 UNIONSELECT7 UNIONSELECT8 UNIONSELECT9)hundreds)tWHEREDATE('2020-01-01') +INTERVALt.numDAY <='2020-12-31') datesWHEREYEAR(date)=2020; 但这里需要注意,WEEK函数返回的周数可能跨年,所以需要确保周属于目标年份。例如,ISO周数可能属于下一年的第一周,但日期仍然在目标年份的末尾。因此,可能需要使用ISO年份和周数的组合。例如,在MySQL中,可以使用YEARWEEK(date,3)来获取ISO年份和周数,然后提取年份部分,判断是否等于目标年份,再统计周数数量。 例如: SELECTCOUNT(DISTINCTWEEK(date,3))AS TotalWeeksFROM(--生成所有日期)datesWHERE YEARWEEK(date,3) DIV100=2020; --YEARWEEK返回的格式是YYYYWW,因此除以100得到年份这样,可以确保只统计属于目标年份的ISO周数,然后统计不同的周数数量,即为该年的总周数。因此,最终的SQL可能需要根据具体的数据库系统和周数标准进行调整。用户可能需要根据他们使用的数据库(如MySQLSQLServer等)来调整函数和生成日期的方法。此外,生成日期的方法在不同数据库中有不同的实现,比如在MySQL中使用递归CTE或辅助表,在SQLServer中使用spt_values,在Hive中使用posexplode等。因此,需要根据具体情况选择合适的方法。综上所述,回答用户的问题需要明确周数的计算标准,生成该年所有日期计算每个日期的周数,并统计该年内的周数数量。同时,考虑到跨年周的问题,需要确保只统计属于目标年份的周数。</think>### 分步解答 要计算一年有多少个星期(自然周数),需根据具体需确定周的定义。以下是基于**ISO周数标准**(每周从周一开始,第一周包含当年至少4天)的通用方法: #### 步骤1:生成全年日期序列 首先生成目标年份的所有日期。以MySQL为例,使用递归CTE或辅助表: ```sql WITH RECURSIVE date_generator AS ( SELECT '2020-01-01' AS date UNION ALL SELECT date + INTERVAL 1 DAY FROM date_generator WHERE date < '2020-12-31' ) SELECT * FROM date_generator; ``` #### 步骤2:计算每个日期的ISO周数 使用`YEARWEEK(date, 3)`函数获取ISO周数(模式3表示从周一开始,且第一周至少4天): ```sql SELECT date, YEARWEEK(date, 3) AS iso_year_week FROM date_generator; ``` #### 步骤3:统计有效周数 筛选属于目标年份的周数,并统计唯一周数数量: ```sql SELECT COUNT(DISTINCT iso_week) AS total_weeks FROM ( SELECT YEARWEEK(date, 3) AS iso_year_week, YEARWEEK(date, 3) DIV 100 AS iso_year, YEARWEEK(date, 3) % 100 AS iso_week FROM date_generator ) t WHERE iso_year = 2020; ``` #### 完整SQL示例(MySQL): ```sql WITH RECURSIVE date_generator AS ( SELECT '2020-01-01' AS date UNION ALL SELECT date + INTERVAL 1 DAY FROM date_generator WHERE date < '2020-12-31' ) SELECT COUNT(DISTINCT iso_week) AS total_weeks FROM ( SELECT YEARWEEK(date, 3) DIV 100 AS iso_year, YEARWEEK(date, 3) % 100 AS iso_week FROM date_generator ) t WHERE iso_year = 2020; ``` ### 结果说明 - 对于2020年(闰年),ISO周数为**53周**,因为12月31日属于第53周[^1]。 - 对于非闰年(如2021年),结果为52周。 ### 注意事项 1. **周数标准**:不同数据库的周数函数参数不同(如MySQL的`WEEK()`模式参数)。 2. **跨年周处理**:需通过`YEARWEEK()`的年份部分过滤跨年周[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值