Get correct week number for december 31st - sql-server

In SQL Server, I am trying to get the week number (with the year) of a given date, European style, so I'm using DATEPART with ISO_WEEK argument:
SELECT CAST(DATEPART(year, myDate) AS VARCHAR) + RIGHT('0' + CAST(DATEPART(ISO_WEEK, myDate) AS VARCHAR), 2);
This works well, except for December 31st of 2018, which falls on the 1st week of 2019, but since I'm using DATEPART for the year and the week separately, this obviously can't work. Here's an example when myDate is 31-12-2018:
SELECT CAST(DATEPART(year, '31-12-2018') AS VARCHAR) + RIGHT('0' + CAST(DATEPART(ISO_WEEK, '31-12-2018') AS VARCHAR), 2);
The above query returns '201801'.
Is there a way to simply get 201901 for December 31st 2018, without testing explicitly for this date ?

See the possible duplicate reference and a little tidy up:
declare #d datetime = '2018-12-31';
select cast(year(dateadd(day, 26 - datepart(ISO_WEEK, #d), #d)) as varchar(4)) + right(('0' + cast(datepart(ISO_WEEK, #d) as varchar(2))), 2)
results in 201901.
Test code:
select cast(ds.d as date) as 'date', cast(year(dateadd(day, 26 - datepart(ISO_WEEK, ds.d), ds.d)) as varchar(4)) + right(('0' + cast(datepart(ISO_WEEK, ds.d) as varchar(2))), 2) as "IsoWeek"
from (
select dateadd(day, x.num, '2018-12-30') as d
from (
select h.num + d.num + s.num as num
from (
select 0 num union select 100 num union select 200 num union select 300 num
) h
cross join (
select 0 num union select 10 num union select 20 num union select 30 num
) d
cross join (
select 0 num union select 1 num union select 2 union select 3 num
) s
) x
) ds
order by ds.d
which results in:
date IsoWeek
2018-12-30 201852
2018-12-31 201901
2019-01-01 201901
2019-01-02 201901
2019-01-09 201902
2019-01-10 201902
2019-01-11 201902
2019-01-12 201902
2019-01-19 201903
2019-01-20 201903
2019-01-21 201904
2019-01-22 201904
2019-01-29 201905
2019-01-30 201905
2019-01-31 201905
2019-02-01 201905
2019-04-09 201915
2019-04-10 201915
2019-04-11 201915
2019-04-12 201915
2019-04-19 201916
2019-04-20 201916
2019-04-21 201916
2019-04-22 201917
2019-04-29 201918
2019-04-30 201918
2019-05-01 201918
2019-05-02 201918
2019-05-09 201919
2019-05-10 201919
2019-05-11 201919
2019-05-12 201919
2019-07-18 201929
2019-07-19 201929
2019-07-20 201929
2019-07-21 201929
2019-07-28 201930
2019-07-29 201931
2019-07-30 201931
2019-07-31 201931
2019-08-07 201932
2019-08-08 201932
2019-08-09 201932
2019-08-10 201932
2019-08-17 201933
2019-08-18 201933
2019-08-19 201934
2019-08-20 201934
2019-10-26 201943
2019-10-27 201943
2019-10-28 201944
2019-10-29 201944
2019-11-05 201945
2019-11-06 201945
2019-11-07 201945
2019-11-08 201945
2019-11-15 201946
2019-11-16 201946
2019-11-17 201946
2019-11-18 201947
2019-11-25 201948
2019-11-26 201948
2019-11-27 201948
2019-11-28 201948

the problem is that there is a unique case where the week number can be very 'early' in the year, although it is actually month 12, therefore it is easy to check if there is a start of year week in December, and in that case simply add one to the year,
DECLARE #mydate as datetime2 = '20270101';
SELECT CAST(DATEPART(year, #myDate)
+ case when DATEPART(ISO_WEEK, #myDate) < 2 and month(#mydate) =12 then
1
else
0
end
+ case when DATEPART(ISO_WEEK, #myDate) >10 and month(#mydate) =1 then
-1
else
0
end
AS VARCHAR)
+ RIGHT('0' + CAST(DATEPART(ISO_WEEK, #myDate) AS VARCHAR), 2);
I added a correction for the 2027 problem highlighted in another answer.

Check following query, its using last day of the week to get the year value
SELECT CAST(DATEPART(year, DATEADD(wk, 1, DATEADD(DAY, 0-DATEPART(WEEKDAY, '2018-12-31'), DATEDIFF(dd, 0, '2018-12-31')))) AS VARCHAR) + RIGHT('0' + CAST(DATEPART(ISO_WEEK, '2018-12-31') AS VARCHAR), 2);

Related

Count number of rows each week with a given starting date in SQL Server

I want to select counts of each order per week. Starting from a given date. Given this set of data below from SQL Server. How can I query it that will show the list of weeks and the corresponding count of rows/orders.
This is my desired output
You can group by DATEPART(wk). Please see DATEPART (Transact-SQL).
It could be something like:
select DATEPART(yyyy,created_by_dt) as Year, DATEPART(wk, created_by_dt) as Week, count(*) as Count from X
group by DATEPART(yyyy,created_by_dt), DATEPART(wk, created_by_dt)
order by DATEPART(yyyy,created_by_dt), DATEPART(wk, created_by_dt)
The following script will give you a nicer output:
select
-- Start of week from https://stackoverflow.com/questions/1267126/get-the-week-start-date-and-week-end-date-from-week-number-in-sql-server
-- by Tomalak
CONVERT(date, DATEADD(dd, 0 - (##DATEFIRST + 5 + DATEPART(dw, created_by_dt)) % 7, created_by_dt)) AS Monday,
count(*) as Count,
MIN(created_by_dt) AS FirstRecordThisWeek,
CONVERT(date,MIN(created_by_dt)) AS FirstRecordThisWeek_date
from X
group by CONVERT(date, DATEADD(dd, 0 - (##DATEFIRST + 5 + DATEPART(dw, created_by_dt)) % 7, created_by_dt))
order by CONVERT(date, DATEADD(dd, 0 - (##DATEFIRST + 5 + DATEPART(dw, created_by_dt)) % 7, created_by_dt))
Output (from my data):
Monday Count FirstRecordThisWeek FirstRecordThisWeek_date
2018-12-31 1 2019-01-03 05:11:30.570 2019-01-03
2019-03-11 1 2019-03-13 06:03:41.500 2019-03-13
2019-04-01 2 2019-04-04 01:16:34.440 2019-04-04
2019-04-15 21 2019-04-15 04:25:30.913 2019-04-15
2019-04-29 6 2019-04-30 06:52:26.057 2019-04-30
2019-05-06 3 2019-05-08 07:01:00.683 2019-05-08
2019-05-13 5 2019-05-16 22:58:43.610 2019-05-16

How to update a date column from column with weeks

I need to set the date to Monday of the week set in the week column
I created a query where there are differences:
select PROD_NR, BEWERK_DAT, BEWERK_WK, DATEPART(wk , BEWERK_DAT) as WeekIndex from Table1 where status = 3 and RIGHT(bewerk_wk,2) <> DATEPART(wk , BEWERK_DAT) order by id desc
Part of the output is like this:
PROD_NR BEWERK_DAT BEWERK_WK WeekIndex
P34619 2020-03-02 2020-09 10
P34619 2020-03-02 2020-09 10
P34619 2020-03-02 2020-09 10
P34619 2020-02-28 2020-08 9
P34619 2020-02-27 2020-08 9
I need to update the BEWERK_DAT column according to the week in BEWERK_WK
Output should be like:
PROD_NR BEWERK_DAT BEWERK_WK WeekIndex
P34619 2020-02-24 2020-09 9
P34619 2020-02-24 2020-09 9
P34619 2020-02-24 2020-09 9
P34619 2020-02-17 2020-08 8
P34619 2020-02-17 2020-08 8
Something like:
UPDATE Table1 SET bewerk_dat = "monday of the week part from BEWERK_WK" where there are differences.
The following may give you what you are looking for. I have provided a SELECT as well as the UPDATE.
The SET DATEFIRST is setting the first of the week to Monday
SET DATEFIRST 1
---
SELECT PROD_NR, BEWERK_DAT, BEWERK_WK , WeekIndex, CAST(SUBSTRING(BEWERK_WK,1,4) AS int) NewYear, CAST(SUBSTRING(BEWERK_WK,6,2) as int) NewWeek
,dateadd (week, CAST(SUBSTRING(BEWERK_WK,6,2) as int), dateadd (year, CAST(SUBSTRING(BEWERK_WK,1,4) AS int)-1900, 0)) - 4 -
datepart(dw, dateadd (week, CAST(SUBSTRING(BEWERK_WK,6,2) as int), dateadd (year, CAST(SUBSTRING(BEWERK_WK,1,4) AS int)-1900, 0)) - 4) + 1
FROM Table1
WHERE CAST(SUBSTRING(BEWERK_WK,6,2) as int) <> WeekIndex
---
UPDATE Table1 SET BEWERK_DAT = dateadd (week, CAST(SUBSTRING(BEWERK_WK,6,2) as int), dateadd (year, CAST(SUBSTRING(BEWERK_WK,1,4) AS int)-1900, 0)) - 4 -
datepart(dw, dateadd (week, CAST(SUBSTRING(BEWERK_WK,6,2) as int), dateadd (year, CAST(SUBSTRING(BEWERK_WK,1,4) AS int)-1900, 0)) - 4) + 1
WHERE CAST(SUBSTRING(BEWERK_WK,6,2) as int) <> WeekIndex
I found a way myself after a few hours more of searching.
set datefirst 1
go
update Table1 set BEWERK_DAT = CAST(DATEADD(WEEK, CONVERT(int, RIGHT(bewerk_wk,2)) - 1,DATEADD(dd, 1 - DATEPART(dw, '1/1/' + CONVERT(VARCHAR(4),CONVERT(int, LEFT(bewerk_wk,4)))), '1/1/' + CONVERT(VARCHAR(4),CONVERT(int, LEFT(bewerk_wk,4))))) as date)
where status = 3 and RIGHT(bewerk_wk,2) <> DATEPART(wk , BEWERK_DAT)
and before I could do this I had some fields in BEWERK_WK with a 9999-99 value which I had to reset first
update Table1 set BEWERK_WK = CONCAT(CONVERT(varchar, DATEPART(year, BEWERK_DAT)), '-', CONVERT(varchar, DATEPART(wk, BEWERK_DAT))) where bewerk_wk like '9%'

How can I convert YYWW to date format based on day[Ex: Monday's date in given YYWW ] in SqlServer?

I have a column in my table with YYWW format. I need to convert this YYWW and get Monday's date.
For Example:
Input YYWW: 1847
Expected Output: 2018-11-19 [Monday's date in 2018 Week 47]
Thanks in advance
I tried the below but does not work properly
declare #value int = 1519
SELECT CONVERT(VARCHAR(10), DATEADD(YEAR, 2000 + #value / 100-1900, 7 * (#value % 100)-7), 105);
1851 -Expected 17-12-2018[Monday] Works fine for this year 2018
1752 -Expected 25-12-2017[Monday] but shows 24-12-2017 [Sunday]
1652 -Expected 26-12-2016 [Monday] but shows 24-12-2016 [Saturday]
1519 -Expected 04-05-2015 [Monday] but shows 07-05-2015 [Thursday]
Try this:
DECLARE #t table(YYWW char(4))
INSERT #t values('1847'),('1752'),('1652'),('1519')
SELECT
CAST(DATEADD(wk,RIGHT(YYWW,2)+DATEDIFF(d,0,DATEADD(
d,-4,LEFT(YYWW,2)+'0101'))/7,0) as date)
FROM #t
Result:
2018-11-19
2017-12-25
2016-12-26
2015-05-04
EDIT:
To get the requested format DD-MM-YYYY:
SELECT
CONVERT(CHAR(10),DATEADD(wk,RIGHT(YYWW,2)+DATEDIFF(d,0,DATEADD(
d,-4,LEFT(YYWW,2)+'0101'))/7,0),105)
FROM #t
I would, personally, use a calendar table. Then you can do something like:
SELECT YT.YYWW,
CT.[date]
FROM YourTable YT
JOIN CalendarTable CT ON CT.[Year] = '20'+LEFT(YT.YYWW,2)
AND CT.WeekNo = RIGHT(YT.YYWW,2)
AND CT.DayOfWeek = 1; --Assumes Monday is day 1.
I think your calc may be off week 47 of 2018 starts on 2018-11-26 which is a Monday.
in any case the following should work if you disagree with the above simply subtract 1 from the number of weeks
DECLARE #Date date
DECLARE #Year int = 2000 +18
declare #week int = 47
SET #Date = DATEADD(YEAR, #Year - 1900, 0)
SELECT dateadd(ww,#week-1,DATEADD(DAY, (##DATEFIRST - DATEPART(WEEKDAY, #Date) + (8 - ##DATEFIRST) * 2) % 7, #Date))

SQL count where between dates by month

Consider the below data:
ID Reference Manager LeaseFirstStart LeaseStop
1 KLEIN John 2008-04-02 00:00:00.000 2010-04-01 00:00:00.000
2 HAWKER John 2008-12-18 00:00:00.000 2010-09-17 00:00:00.000
3 SLEEP Bob 2008-01-23 00:00:00.000 2009-01-22 00:00:00.000
4 CODD Bob 2009-08-03 00:00:00.000 2010-08-02 00:00:00.000
5 ALLEN Bob 2008-01-30 00:00:00.000 2009-07-31 00:00:00.000
The earliest month is Jan 2008 and the latest month is Sep 2010.
How can I count the number of leases that were current per month? The output should look like this:
Month Number of Leases
2008-01 2
2008-02 2
2008-03 2
2008-04 3
2008-05 3
2008-06 3
2008-07 3
2008-08 4
… …
Ultimately, I want to use the answer to the question to create the dataset below for use in excel by the user so they can see who had how many leases during the data period.
Month Manager Number of Leases
2008-01 Bob 2
2008-01 John 0
2008-02 Bob 2
2008-02 John 0
2008-03 Bob 2
2008-03 John 0
2008-04 Bob 2
2008-04 John 1
2008-05 Bob 2
2008-05 John 1
2008-06 Bob 2
2008-06 John 1
2008-07 Bob 2
2008-07 John 1
2008-08 Bob 3
2008-08 John 1
… … …
I know I've done it before, but it was a long time ago and I remember it being messy. Thanks in advance!
select sum (no) as no,datet from ( SELECT COUNT (*) as no ,(convert(varchar,datepart (yyyy,[ Start] )) + '-' + convert(varchar, MONTH([ Start] ))) as datet
FROM <tbl>
GROUP BY (convert(varchar,datepart (yyyy,[ Start] )) + '-' + convert(varchar, MONTH([ Start] )))
union SELECT COUNT (*) as no ,(convert(varchar,datepart (yyyy,[ End] )) + '-' + convert(varchar, MONTH([ End] ))) as datet
FROM <tbl>
GROUP BY (convert(varchar,datepart (yyyy,[ End] )) + '-' + convert(varchar, MONTH([ End] )) ) ) t
This is very logical question, finally I created the sql which gives the desired result.. I verified every date and month count and its all ok.
Declare #t table (ID int, Reference varchar(50), Manager varchar(50),LeaseFirstStart datetime,LeaseStop datetime)
insert into #t
values
(1,'KLEIN','John','2008-04-02 00:00:00.000','2010-04-01 00:00:00.000'),
(2,'HAWKER','John','2008-12-18 00:00:00.000','2010-09-17 00:00:00.000'),
(3,'SLEEP','Bob','2008-01-23 00:00:00.000','2009-01-22 00:00:00.000'),
(4,'CODD','Bob','2009-08-03 00:00:00.000','2010-08-02 00:00:00.000'),
(5,'ALLEN','Bob','2008-02-28 00:00:00.000','2009-07-31 00:00:00.000')
declare #lowerdate datetime , #currentdt datetime
select #lowerdate = min(leasefirststart), #currentdt= max(leasestop) from #t
;with cte as
(
select firstday,DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, FirstDay) + 1, 0)) Lastday, mng from
( select dateadd(m,datediff(m,0,#lowerdate)+v.number,0) as FirstDay
From master..spt_values v
Where v.type='P' and v.number between 0 and datediff(m, #lowerdate, #currentdt)
) as a
, (select distinct manager mng from #t ) as b
)
select (convert(varchar,datepart (yyyy,FirstDay )) + '-' + convert(varchar, MONTH(FirstDay ))) MonthAndYear ,mng as mng , count( manager ) cnt
from cte
left join #t on
(
firstday between LeaseFirstStart and LeaseStop
or
Lastday between LeaseFirstStart and LeaseStop
) and cte.mng = Manager
group by firstday, mng
order by FirstDay

Calculate total running time from start/stop timestamps

I have a table containing a number of timestamps per day, they represents start and stop events.
ID TimeStamp
----------------------
1 2008-01-01 07:00:00
1 2008-01-01 08:15:00
1 2008-01-01 10:00:00
1 2008-01-01 11:00:00
1 2008-01-02 10:30:00
1 2008-01-02 12:00:00
I would like to calcuate the total running time per day, like this:
ID Date RunningTime
-------------------------
1 2008-01-01 02:15:00
1 2008-01-02 01:30:00
Do anyone have a nice T-SQL solution for my problem?
WITH q AS
(
SELECT *,
CONVERT(DATETIME, CONVERT(VARCHAR(8), TimeStamp, 112), 112) AS dte,
ROW_NUMBER() OVER (PARTITION BY id, CONVERT(DATETIME, CONVERT(VARCHAR(8), TimeStamp, 112), 112) ORDER BY TimeStamp) AS rn
FROM mytable
)
SELECT qb.id, qb.dte, SUM(DATEDIFF(second, qb.TimeStamp, qe.TimeStamp))
FROM q qb
JOIN q qe
ON qe.id = qb.id
AND qe.dte = qb.dte
AND qe.rn = qb.rn + 1
WHERE qb.rn % 2 = 1
GROUP BY
qb.id, qb.dte
This assumes that every record open on a certain day should also be closed on the same day.

Resources