Set week number based on first Monday of Year - sql

I have a requirement to set the week number of a table from the first day to the first Monday to the next Monday and so on. I can easily get the first Day and first Monday of year but I do not know how to increment trough the table in 7 days intervals from the first Monday so that I can set the week number.
I have something like this:
UPDATE table
SET weeknumberofyear = #WeekNumber + 1
WHERE datefield = DATEADD(Day,7,(SELECT DATEADD(DAY, (##DATEFIRST - DATEPART(WEEKDAY, #Date) + (8 - ##DATEFIRST) * 2) % 7, #Date)))

Since the datepart(week,datefield) function gets week number based on Sunday as the first day of the week, all you have to do is check datepart(weekday,datefield) and if it is 1 (Sunday) or 2 (Monday), subtract 1 from the datepart(week,datefield) function:
update table
set weeknumberofyear = datepart(week,datefield) -
case when datepart(weekday,datefield) in(1,2) then 1 else 0 end
EDIT This doesn't account for Years when Sunday or Monday are the first day of the year. In those cases, you would get 0 for weeknumberofyear. To fix this, perform a second update to your table. Even though this takes two updates, I still think it is more efficient than cycling through all the records.
update table
set weeknumberoftheyear = weeknumberoftheyear + 1
where year(datefield) in(
select distinct year(datefield)
from table
where weeknumberoftheyear = 0
)
EDIT WeekNumberOfTheMonth Update - Now that we have the WeekNumberOfTheYear value, we can use a ranking function on that field to update the WeekNumberOfTheMonth column without any recursion.
update t
set t.weeknumberofthemonth = u.weeknumberofthemonth
from table t
inner join (
select distinct weeknumberoftheyear,
dense_rank() over(partition by month(datefield)
order by weeknumberoftheyear) weeknumberofthemonth
from table ) u
on u.weeknumberofyear = t.weeknumberofyear

I'm not sure what you mean by all the talk about "Monday", but if you're looking to get the week number from a date, you can do something like this:
UPDATE table
SET weeknumberofyear = DATEPART(wk, datefield)

Related

Get the previous month number including year

I have a table in which I get the coupons of a specific company.
My coupon table is like:
Company_Coupon
ID
CompanyID
Month
Total_Coupons
Year
I keep my months in the format of their number (1-12).
When I update the total coupons for the invoicing, I want to update the ones from the previous month.
I do this by the following query:
UPDATE Company_Coupon
SET Total_Coupons = #count
WHERE CompanyID = 1205
AND Month = MONTH(GETDATE())-1 AND Year = YEAR (GETDATE())
My query works but I noticed that this won't work in January 2019.
How can I update this query so that it will work in January 2019?
Try with case when like below:
UPDATE Company_Coupon
SET Total_Coupons = #count
WHERE CompanyID = 1205
AND Month = (case when MONTH(GETDATE())-1=0 then 12 else MONTH(GETDATE())-1 end) AND Year = (case when MONTH(GETDATE())-1=0 then YEAR (GETDATE())-1 else YEAR (GETDATE()) end)
You can try to get different number between 1900-01-01 and your data, then do some calculation to get last month.
Query 1:
SELECT DATEADD(month, DATEDIFF(month,0,'2019-01-01') - 1, 0)
UNION ALL
SELECT DATEADD(month, DATEDIFF(month,0,'2018-08-01') - 1, 0)
Results:
| |
|----------------------|
| 2018-12-01T00:00:00Z |
| 2018-07-01T00:00:00Z |
so you query can be
UPDATE Company_Coupon
SET Total_Coupons = #count
WHERE
CompanyID = 1205
AND
Month = MONTH(DATEADD(month, DATEDIFF(month,0,GETDATE()) - 1, 0))
AND
Year = YEAR (DATEADD(month, DATEDIFF(month,0,GETDATE()) - 1, 0))
As I said in a comment, I'd prefer to have a single YearMonth column with the correct data type for datetime work, but we can do something very similar here:
UPDATE Company_Coupon
SET Total_Coupons = #count
FROM Company_Coupon
CROSS APPLY (SELECT
DATEADD(month,DATEDIFF(month,'20010201',GETDATE()),'20010101')) t(LastMonth)
WHERE CompanyID = 1205
AND Month = MONTH(LastMonth) AND Year = YEAR(LastMonth)
The two dates used in the above expression do not matter much. All that matters really is the relationship between them. Here, the second date falls a month before the first and it's the relationship that effectively gets applied to GETDATE() by the DATEADD/DATEDIFF expression. It's a pattern that can be used in lots of different ways - e.g. a variant of this pattern can be used to find the last day of 3 months ago if you're not on a SQL version that supports EOMONTH.
If you use SQL Server 2012 or later version, you can employ the eomonth() function that returns last day of a previous month for a given date. From it, you can extract both month() and year() parts and use them in your query:
UPDATE c SET Total_Coupons = #count
from dbo.Company_Coupon c
WHERE c.CompanyID = 1205
AND c.Month = MONTH(eomonth(GETDATE()))
AND c.Year = YEAR(eomonth(GETDATE()));

Populate null records scenario

I have a sql table the following columns:
FirstName, LastName, Points, StartTime
I have the data right now with the StartTime populated for the person with the highest points. StartTime is Null for everyone else.
I want to do the following in a stored procedure:
Populate StartTime with intervals of 30 minutes. So right now there is only one person with StartTime. The next person in line in terms of points gets StartDate 30 minutes after the previous one. There are some conditions. Right now StartTime for the person with highest points is set to a working day and time is 8:00AM. The next one should be 8:30AM and so on. The last time for a working day is 5:00PM and then it should go to the next working day (skipping weekends) and continue assigning dates and times starting from 8:00AM to 5:00PM with 30 minutes increment - Only work days until all the StartTimes for all the rows are populated.
So there should be 18 people with a starttime for the same working day.
Any ideas?
Thank you
Because of the fairly simple repeating pattern you can generate start times using some simple maths. Assuming a working week of Monday to Friday, this SQL will generate the available slot date/times;
declare #start_point datetime = '20180524 08:00' -- this MUST be a Monday date at 8am
declare #slot_duration int = 30 -- minutes
declare #slots_per_day int = 18
declare #slots_per_week int = #slots_per_day * 5
;with numbers as (select row_number() over (order by so1.object_id) - 1 as rn from sys.objects so1 cross join sys.objects so2)
select
rn,
rn / #slots_per_week as weeks, -- zero based number
(rn % #slots_per_week) / #slots_per_day days, -- zero based number
rn % #slots_per_day as slots, -- zero based number
dateadd(minute, (rn % #slots_per_day) * #slot_duration,
dateadd(day, (rn % #slots_per_week) / #slots_per_day,
dateadd(week, rn / #slots_per_week, #start_point)
)
) as StartTime
from
numbers
order by rn
Now you can join to this data (subquery, temp table etc) to update your original table.

Retrieve records between a current date and previous date

I need to get a count of the # of rows resulting from a query which needs to have the below logic:
Assume the table includes 3 columns for now; ID, VALUE and INSERT DATE
Records inserted on
Current day-1
Minus
records inserted on
the latest business day prior to the (current day-1)
To add more details:
****I am looking for 'number of records' inserted between 2 dates i.e. if 200 records were inserted between Thursday and Friday then when I run the query on Monday my result should show me '200 records'.
Assumption: Business days = Mon-Fri
USE DATEADD Function:
Select
*
FROM
TableName
WHERE CreatedDate Between CAST(DATEADD(d,-1,GETDATE() AS Date) AND CAST( GETDATE() AS Date)
Please try this
Select * FROM TableName WHERE AddedDate Between DATEADD(day,-1,GETDATE()) and GETDATE()

Algorithm to get next weekday based on today

Suppose today is Thursday. I have 1 flag for both Wednesday and Friday.
If I create a temporary table then it will look like
ID WeekDay XFlag
==================================
1 Mon 0
2 Tue 0
3 Wed 1
4 Thu 0
5 Fri 1
6 Sat 0
7 Sun 0
Now, as we assumed today is Thursday, the next day when XFlag value 1 is Friday.
Any or all weekdays can be marked/flagged 1. But, I ultimately want to get the next weekday based on today. So, my result will be Fri for this in a varchar variable in a stored procedure.
Here, if today is wed then also result will be "Fri". But if today is "Fri" result will be "Wed". So, please care for such cases also.
How can I do it?
So you want the next day but the challenge is that the week days form a cycle. You can handle this using order by and some cleverness:
select top 1 tt.*
from TemporaryTable tt
where flag = 1
order by (7 + tt.id - datepart(dw, getdate())) % 7
Here is a SQL Fiddle.
EDIT:
If datefirst might be set differently, you can do the join on the date name. Just a bit more complicated with the order by condition:
select top 1 tt.*
from TemporaryTable tt cross join
(select id from TemporaryTable tt where Weekday = left(datepart(dw, getdate()), 3)
) as startid
where flag = 1
order by (tt.id - startid.id + 7) % 7;
This assumes, of course, that the language being returned is English.
I've gone quite procedural here, but the parts can be incorporated into a larger query, rather than using local variables, if required:
declare #t table (ID int not null,Weekday char(3) not null,XFlag bit not null)
insert into #t(ID,WeekDay,XFlag) values
(1,'Mon',0),(2,'Tue',0),(3,'Wed',1),
(4,'Thu',0),(5,'Fri',1),(6,'Sat',0),
(7,'Sun',0)
declare #Today int
declare #NextDay int
--Set today, in a DATEFIRST safe manner
set #Today = ((DATEPART(weekday,CURRENT_TIMESTAMP) + 7) --Today
- DATEPART(weekday,'20140106') --Known Monday
) % 7 + 1
set #NextDay = COALESCE((select MIN(ID) from #t where XFlag = 1 and ID > #Today),
(select MIN(ID) from #t where XFlag = 1))
select Weekday from #t where ID = #NextDay
Hopefully it's (relatively) easy to see how I'm thinking.
Setting #Today is probably the most complex part, and that only because I'm trying to write code that can be run by anyone, anywhere, without having to adjust either it or their DATEFIRST setting. We subtract the results of two calls to DATEPART(weekday,... where we know that one of them is definitely a Monday, and we've also set it up so that we always produce a positive result. We then use % 7 to ensure that it's in the range 0-6, corresponding to Monday-Sunday, and then add 1 so that the values produced by this expression match the IDs in your table.
Slightly improved version of #GordonLinoff's answer that doesn't rely on local datefirst settings
select top 1 *
from <table>
where Xflag = 1
order by datediff(d, id-1, current_timestamp) % 7 desc
Try this Here WorkingDay is TableName
Select *,
CASE
WHEN
(Select top 1 WeekDay From WorkingDay WHERE ID > W.ID AND XFLAG=1 ORDER BY ID) IS NOT NULL
THEN (Select top 1 WeekDay From WorkingDay WHERE ID > W.ID AND XFLAG=1 ORDER BY ID)
ELSE
(Select top 1 WeekDay From WorkingDay WHERE ID < W.ID AND XFLAG=1 ORDER BY ID)
END AS NextWorkingDay
From WorkingDay W
Order By ID

Return just the last day of each month with SQL

I have a table that contains multiple records for each day of the month, over a number of years. Can someone help me out in writing a query that will only return the last day of each month.
SQL Server (other DBMS will work the same or very similarly):
SELECT
*
FROM
YourTable
WHERE
DateField IN (
SELECT MAX(DateField)
FROM YourTable
GROUP BY MONTH(DateField), YEAR(DateField)
)
An index on DateField is helpful here.
PS: If your DateField contains time values, the above will give you the very last record of every month, not the last day's worth of records. In this case use a method to reduce a datetime to its date value before doing the comparison, for example this one.
The easiest way I could find to identify if a date field in the table is the end of the month, is simply adding one day and checking if that day is 1.
where DAY(DATEADD(day, 1, AsOfDate)) = 1
If you use that as your condition (assuming AsOfDate is the date field you are looking for), then it will only returns records where AsOfDate is the last day of the month.
Use the EOMONTH() function if it's available to you (E.g. SQL Server). It returns the last date in a month given a date.
select distinct
Date
from DateTable
Where Date = EOMONTH(Date)
Or, you can use some date math.
select distinct
Date
from DateTable
where Date = DATEADD(MONTH, DATEDIFF(MONTH, -1, Date)-1, -1)
In SQL Server, this is how I usually get to the last day of the month relative to an arbitrary point in time:
select dateadd(day,-day(dateadd(month,1,current_timestamp)) , dateadd(month,1,current_timestamp) )
In a nutshell:
From your reference point-in-time,
Add 1 month,
Then, from the resulting value, subtract its day-of-the-month in days.
Voila! You've the the last day of the month containing your reference point in time.
Getting the 1st day of the month is simpler:
select dateadd(day,-(day(current_timestamp)-1),current_timestamp)
From your reference point-in-time,
subtract (in days), 1 less than the current day-of-the-month component.
Stripping off/normalizing the extraneous time component is left as an exercise for the reader.
A simple way to get the last day of month is to get the first day of the next month and subtract 1.
This should work on Oracle DB
select distinct last_day(trunc(sysdate - rownum)) dt
from dual
connect by rownum < 430
order by 1
I did the following and it worked out great. I also wanted the Maximum Date for the Current Month. Here is what I my output is. Notice the last date for July which is 24th. I pulled it on 7/24/2017, hence the result
Year Month KPI_Date
2017 4 2017-04-28
2017 5 2017-05-31
2017 6 2017-06-30
2017 7 2017-07-24
SELECT B.Year ,
B.Month ,
MAX(DateField) KPI_Date
FROM Table A
INNER JOIN ( SELECT DISTINCT
YEAR(EOMONTH(DateField)) year ,
MONTH(EOMONTH(DateField)) month
FROM Table
) B ON YEAR(A.DateField) = B.year
AND MONTH(A.DateField) = B.Month
GROUP BY B.Year ,
B.Month
SELECT * FROM YourTableName WHERE anyfilter
AND "DATE" IN (SELECT MAX(NameofDATE_Column) FROM YourTableName WHERE
anyfilter GROUP BY
TO_CHAR(NameofDATE_Column,'MONTH'),TO_CHAR(NameofDATE_Column,'YYYY'));
Note: this answer does apply for Oracle DB
Here's how I just solved this. day_date is the date field, calendar is the table that holds the dates.
SELECT cast(datepart(year, day_date) AS VARCHAR)
+ '-'
+ cast(datepart(month, day_date) AS VARCHAR)
+ '-'
+ cast(max(DATEPART(day, day_date)) AS VARCHAR) 'DATE'
FROM calendar
GROUP BY datepart(year, day_date)
,datepart(month, day_date)
ORDER BY 1