SQL - Getting day for the whole week - tsql

I want to get the whole day of the week depend on the date, my query is working and getting the result that I want but when the date value is Sunday, result changes.
I'm starting the result from Mon to Sunday.
Examples below:
My Code:
SELECT UserID,Scdl_TkIN as TimeIn, Scdl_TkOut as [TimeOut]
FROM EmployeeTimekeeping
WHERE CONVERT(DATE,Scdl_TkIN) >= dateadd(day, 2-datepart(dw, '2022-04-23'),CONVERT(date,'2022-04-23'))
AND CONVERT(DATE,Scdl_TkIN) < dateadd(day, 9-datepart(dw, '2022-04-23'), CONVERT(date,'2022-04-23'))AND UserID ='15020009'
ORDER BY CONVERT(DATE,Scdl_TkIN)
1st display is correct, but when I change the value into '2022-04-24' , the result is now the second pic but I want the result still 1st pic.

If I got it right you want the whole week of data given a single date.
I'm not 100% sure about your date logic and I'd rather use the WEEK as a filter as it seems clearer, that said the issue you have is the value of SELECT ##DATEFIRST.
By default its value is 7, meaning that Sunday is considered the first day of the week, that's why you get that "unexpected" result.
here is my solution, but just setting SET DATEFIRST 1; should give you the expected result.
SET DATEFIRST 1;
SELECT
UserID
,Scdl_TkIN as TimeIn
,Scdl_TkOut as TimeOut
FROM EmployeeTimekeeping
WHERE
DATEPART(WEEK,Scdl_TkIN) = DATEPART(WEEK,'2022-04-23')
AND YEAR(Scdl_TkIN) = YEAR('2022-04-23')
AND UserID ='15020009'
ORDER BY
Scdl_TkIN
Note: if you decide to use WEEK for filtering you will have to choose between WEEK and ISO_WEEK
Edit: when using week you must also consider the year in the filter

Related

How to pull info from following month, for each month

I am having trouble with a task in Tableau. I need to pull in a value for each month, but the value originates in the following month (month + 1). For example, in January, I need to pull a beginning value from February, and then in February I need to pull a beginning value from March. The idea is to take the beginning value from the following month to calculate the difference from the ending value from the current month. The data I have is snapshot based, so there is one column which is the Date column, which can be 1/1/2020, or 2/1/2020 etc. (its done by month, not days).
My instinct is to create a calculated field and say something along the lines of 'where snapshot = snapshot (current) + 1 month, take in this value' but that does not work because they are not equal. I need to create a separate column or LOD for the future month I think? or a parameter?
ex.
{fixed [date], [ID]: SUM( [Date] = Dateadd('month',1,[Date]) Then [Begin Value] End)}
I found the answer. I needed to use the LOOKUP() function.

Extract highest date per month from a list of dates

I have a date column which I am trying to query to return only the largest date per month.
What I currently have, albeit very simple, returns 99% of what I am looking for. For example, If I list the column in ascending order the first entry is 2016-10-17 and ranges up to 2017-10-06.
A point to note is that the last day of every month may not be present in the data, so I'm really just looking to pull back whatever is the "largest" date present for any existing month.
The query I'm running at the moment looks like
SELECT MAX(date_col)
FROM schema_name.table_name
WHERE <condition1>
AND <condition2>
GROUP BY EXTRACT (MONTH FROM date_col)
ORDER BY max;
This does actually return most of what I'm looking for - what I'm actually getting back is
"2016-11-30"
"2016-12-30"
"2017-01-31"
"2017-02-28"
"2017-03-31"
"2017-04-28"
"2017-05-31"
"2017-06-30"
"2017-07-31"
"2017-08-31"
"2017-09-29"
"2017-10-06"
which are indeed the maximal values present for every month in the column. However, the result set doesn't seem to include the maximum date value from October 2016 (The first months worth of data in the column). There are multiple values in the column for that month, ranging up to 2016-10-31.
If anyone could point out why the max value for this month isn't being returned, I'd much appreciate it.
You are grouping by month (1 to 12) rather than by month and year. Since 2017-10-06 is greater than any day in October 2016, that's what you get for the "October" group.
You should
GROUP BY date_trunc('month', date_col)

Return results from full months

I currently have a dataset where the range of results span between the dates: 15-01-2017 to 30-04-2018
So therefore, my min(date) would be 15-01-2017.
However, I want to return results whereby if my min(date) is not a complete month, it should give me the results from next full month onwards (i.e.: 01-02-2017 to 30-04-2018).
Hoping to get some direction
Thank you
This will return the start of the next month unless it's the first of the month:
trunc(add_months(min(datecol)-1,1), 'mon')
Add one month to the day before the min date (i.e. only the 1st will result in the previous month), then get the 1st of that month.
Depending on your actual query you might filter using
qualify datecol >= trunc(add_months(min(datecol) over (partition by ??)-1,1), 'mon')

Tricking Weekofyear in Hive by shifting the week, for counting

I've been working on this problem for a while now. Basically I have a simple set of data with UserId, and TimeStamp. I want to know how many distinct UserId's appear each week, the catch is my week is measured in Sunday-Saturday, NOT Monday - Sunday, which is what Weekofyear() uses.
Right now I'm hardcoding each week and running the query:
SELECT
count(distinct UserId)
FROM data.table
where from_unixtime((CAST(timestamp as BIGINT)))
between TO_DATE("2016-06-05") AND TO_DATE("2016-06-12")
I'm trying to find a way to shift the timestamp back a day to trick weekofyear into thinking my Sunday is actually a Monday, but have not been successful. My latest futile attempt looked like:
SELECT
count(distinct UserId), weekofyear(date_sub(from_unixtime(CAST(timestamp as BIGINT)),1))
FROM table.data
where from_unixtime((CAST(timestamp as BIGINT)))
between TO_DATE("2016-06-01") AND TO_DATE("2016-06-30")
group by weekofyear(date_sub(from_unixtime(CAST(timestamp as BIGINT)),1))
This results in the same numbers as if I didn't subtract a day. I not sure why this isn't working. I feel like there should be a way to manage this. Right now if I wanted to pull all the data by week WHERE X is true, I'd have to manually do each week, that won't be sustainable. Any suggestions on how to work smarter?
Thank you.
Simple Solution
You can simply create your own formula instead of going with pre-defined function for "week of the year"
Advantage: you will be able to take any set of 7 days for a week.
In your case since you want the week should start from Sunday-Saturday we will just need the first date of sunday in a year
eg- In 2016, First Sunday is on '2016-01-03' which is 3rd of Jan'16
--assumption considering the timestamp column in the format 'yyyy-mm-dd'
SELECT
count(distinct UserId), lower(datediff(timestamp,'2016-01-03') / 7) + 1 as week_of_the_year
FROM table.data
where timestamp>='2016-01-03'
group by lower(datediff(timestamp,'2016-01-03') / 7) + 1;

T-SQL Between Dates Confusion

I am working with T-SQL in SQL Server 2000 and I have a table TRANSACTIONS which has a date column TRANDATE defined as DateTime, among many other columns which are irrelevant for this question..
The table is populated with transactions spanning many years. I ran into code, test, that has me confused. There is a simple SELECT, like this:
SELECT TRANDATE, RECEIPTNUMBER FROM TRANSACTIONS WHERE TRANDATE BETWEEN '12/01/2010' and '12/31/2010' ORDER BY TRANDATE
and its not returning two rows of data that I know are in that table.
With the statement above, the last row its returning, in order, has a TRANDATE of:
2010-12-31 00:00:00.000
When I modify the statement like below, I get the additional two rows for December 2010 that are in that table:
SELECT TRANDATE, RECEIPTNUMBER FROM TRANSACTIONS WHERE TRANDATE BETWEEN '12/01/2010 00:00:00' and '12/31/2010 23:59:59' ORDER BY TRANDATE
I have tried to find out why the BETWEEN operator doesnt include ALL rows for the 24 period in 12/31/2010 when using the first SELECT, above. And why does it need to have the explicit hours added to the SELECT statement as in the second, modified, statement to get it to pull the correct number of rows out?
Is it because of the way TRANDATE is defined as "DATETIME"?
Based on this finding, I think that am going to have to go through all of this old code because these BETWEEN operators are littered throughout this old system and it seems like its not pulling all of the data properly. I just wanted clarification from some folks first. Thanks!
A date is a point in time, not a time span.
'12/31/2010' is a point, too. Namely, it's the midnight of the 31st of December.
Everything that happened after this point is ignored.
That's exactly the behaviour you want (even if you haven't realised that yet).
Do not think that when you choose to omit the time part, it is magically assumed to be "any". It's going to be "all zeroes", that is, the midnight.
If you want to include the entire day in your query without having to specify 23:59:59 (which, by the way, excludes the last second of the day, between the moment 23:59:59 of the current day and the moment 00:00:00 of the next day), you can do that either by using strict inequalities (>, <) bounded by the first points of time you don't want:
WHERE TRANDATE >='12/01/2010 00:00:00' and TRANDATE < '01/01/2011'
or by comparing date values casted to DATE:
WHERE CAST(TRANDATE AS DATE) between '12/01/2010' and '12/31/2010'
(it is okay to put this type of cast in a WHERE clause, it is sargable).
As you have discovered, if you don't specify a time when entering a date, it defaults to midnight in the morning of the date. So 12/31/2010 stops at midnight when that day begins.
To get all dates for 12/31/2010, you can either specify the time, as you have done, or add one day to the ending date. Without a time, 1/1/2011 ends at the stroke of midnight on 12/31/2010. So, you could do BETWEEN 12/1/2010 AND 1/1/2011. You can use DATEADD to add the day in your SQL if that makes it easier.
There is some risk in that second approach of adding a day. You will get any records for 1/1/2011 that carry the time of 00:00:00.
Here's one way to perform the DATEADD:
DECLARE #FromDate datetime, #ToDate datetime
// These might be stored procedure input parameters
SET #FromDate = '12/1/2010'
SET #ToDate = '12/31/2010'
SET #ToDate = DATEADD(d, 1, #ToDate)
Then you use #ToDate in your WHERE clause in the BETWEEN phrase in the usual way.
'12/01/2010' means '12/01/2010 00:00:00' and '12/31/2010' means '12/31/2010 00:00:00'. This is why datetime values that fall later on the day on 12/31/2010 are excluded from your query results.
What would be your expected result if I would do this
Insert "12/31/2010" into your datetime column?
Exactly: 12-31-2010 00:00:00
So why would you expect it to be different as argument for a query?
You have kind of answered your own question already. What you have observed is the way SQL Server works.
If it is confirmation you need, this MSDN document has following to say about it
When the time part is unspecified, it
defaults to 12:00 A.M. Note that a row
that contains a time part that is
after 12:00 A.M. on 1998-0105 would
not be returned by this query because
it falls outside the range.
Edit
As for your comment, a datetime essentially is a floating point value.
Following script shows what numbers SQL Server works with.
40541.9749 (12/31/2010 23:23:59) can't be included when your upper bound is 40541 (12/31/2010)
DECLARE #ADateTime1 DATETIME
DECLARE #ADateTime2 DATETIME
DECLARE #ADateTime1AsFloat FLOAT
DECLARE #ADateTime2AsFloat FLOAT
SET #ADateTime1 = '12/31/2010'
SET #ADateTime2 = '12/31/2010 23:23:59'
SET #ADateTime1AsFloat = CAST(#ADateTime1 AS FLOAT)
SET #ADateTime2AsFloat = CAST(#ADateTime2 AS FLOAT)
SELECT #ADateTime1AsFloat, #ADateTime2AsFloat