ApptStart
2005-02-18 10:00:00.000
2005-02-18 13:00:00.000
2005-02-18 11:00:00.000
2005-02-21 09:00:00.000
2005-02-18 15:30:00.000
2005-02-18 14:30:00.000
.
.
.
I have a column in our database that looks like the above. I want to count appointments for the month for a given doc. On Fridays most of them do a half day. So I do not want to count Fridays with appointments only in the morning. If the appointment is in the afternoon, after 12:00:00.000 I want to include that day in the distinct count.
So far I have:
SELECT
ScheduleDays = count(distinct CONVERT(datetime, convert(char(12), a.ApptStart, 1)))
FROM Appointments a
WHERE
ApptKind = 1 AND
--filter on current month
a.ApptStart >= ISNULL(DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0),'1/1/1900') AND
a.ApptStart < ISNULL(DATEADD(month, DATEDIFF(month, 0, GETDATE())+1, 0),'1/1/3000') AND
--Filter on doctor
a.ResourceID in (201)
This worked when I counted every day with appointments on it, but like I said I need to exclude those half days.So I was thinking about only looking at the last right chars of the ApptStart and comparing it x > noon in a case inside the distinct count...
I tried the following, but it did not work:
ScheduleDays = count(distinct case when (Right(a.ApptStart, 12)) > '12:00:00:000' then 1 else 0 END)
Thanks in advance!
EDIT
I tried:
SELECT
ScheduleDays=COUNT(DISTINCT(CAST(datediff(d,0,a.ApptStart) as datetime)))
FROM Appointments a
WHERE
ApptKind = 1 AND
--filter on current month
a.ApptStart >= ISNULL(DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0),'1/1/1900') AND
a.ApptStart < ISNULL(DATEADD(month, DATEDIFF(month, 0, GETDATE())+1, 0),'1/1/3000')AND
--filter all days that aren't Friday, and then give you all Fridays with an hour > 12.
DATENAME(weekday, a.ApptStart) <> 'Friday' OR DATEPART(hour, a.ApptStart) > 12 AND
--Filter on doctor
a.ResourceID in (201)
for 1808 as the count
You probably want to treat dates as dates, rather than strings. You can determine whether a particular timestamp is on a Friday -- or the hour of a timestamp -- using DATEPART, without having to CONVERT it into a CHAR:
datename(weekday, timestamp_value) -- returns Friday
datepart(weekday, timestamp_value) -- returns either 5 or 6, depending on the value of SET DATEFIRST. (Get day of week in SQL 2005/2008)
datepart(hour, timestamp_value) -- returns hour part
Using these, you can test whether a timestamp is on Friday at or after noon by checking if datepart(weekday, timestamp_value) = 6 and datepart(hour, timestamp_value) >= 12.
bendataclear pointed out that you're using distinct on a case statement which can only ever return 0 or 1, so your total will only ever be 0, 1, or 2. If you're trying to determine which days the doctors worked more than half a day, you'll need to select distinct dates --
SELECT COUNT(DISTINCT(CAST(datediff(d,0,timestamp_value) as datetime)))
FROM table_name
WHERE DATENAME(weekday, timestamp_value) <> 'Friday' OR DATEPART(hour, timestamp_value) > 12
AND the rest of your filters here
The WHERE clause there will give you all days that aren't Friday, and then give you all Fridays that have an hour > 12.
Related
How can I select
The past week
Its corresponding days in the year before
This is needed for a dashboard, I would like to show a chart with results from the past seven days. It displays green if our call-center handles 98% of their phone calls within a certain time-span, red if we go over 98%. As a reference I would like to create a chart below with the corresponding seven days in the year before. This is challenging, because weekdays really influence the workload. That means I can't compare a Tuesday with a Sunday or Monday.
For instance, today is Saturday 21st Dec 2019, I would like to report the following timespans:
2019-12-13 00:00:00 -> 2019-12-20 23:59:59
and
2018-12-14 00:00:00 -> 2018-12-21 23:59:59
I made the following code (used within a select statement):
case when cs.ReachedAt between (getdate() - 7) and getdate() then 1 else 0 end as Is_PastWeek
case when cs.ReachedAt between (convert(datetime, convert(varchar(50), convert(date, dateadd(d, -1, dateadd(wk, -52, getdate())))) + ' 23:59:59')) and (convert(datetime, convert(varchar(50), convert(date, dateadd(d, -8, dateadd(wk, -52, getdate())))) + ' 00:00:00')) then 1 else 0 end as Is_SameWeekLastYear
It works, but isn't perfect. I just select the corresponding weekday in the same week as 52 weeks ago. Which means I sometimes end up selecting a matching weekday, but not the nearest. How can I do this better?
EDIT
To clarify what I mean by "picking the nearest corresponding weekday in the year before", i made the following example:
with cte1 as (
select row_number() over (order by (select 1)) - 1 as incrementor
from master.sys.columns sc1
cross join master.sys.columns sc2
), cte2 as (
select dateadd(day, cte1.incrementor, '2000-01-01') as generated_date
from cte1
where dateadd(day, cte1.incrementor, '2000-01-01') < getdate()
), cte3 as (
select convert(date, generated_date) as generated_date
, convert(date, getdate()) as now_date
from cte2
), cte4 as (
select *
, convert(date, dateadd(YEAR, -1, now_date)) as year_back
from cte3
)
select now_date
, generated_date
from cte4
where 1=1
and datepart(week, year_back) = datepart(week, generated_date)
and datepart(DW, year_back) = datepart(DW, generated_date)
This will result in:
For the grey values, I would rather take the weekday of one week later. That way I pick "the nearest corresponding weekday in the year before".
Please note that the above is an example to show what I mean, my ultimate goal is to start with this date, select the whole week before... And all (if possible) neatly within a where clause.
The expression datepart(week, getdate()) will deliver you the calendar week. With this, you can go further.
This is too long for a comment.
What difference does it make? If you are looking for the past week, just look at the same 7 days from the previous year. In one case the week might start on a Tuesday and in the other on a Wednesday. But in both cases, each weekday occurs once.
The logic would be:
where cs.ReachedAt >= datefromparts(year(getdate() - 7) - 1, month(getdate() - 7), day(getdate() - 7) and
cs.ReachedAt < datefromparts(year(getdate()), month(getdate()), day(getdate()))
The logic for the current year:
where cs.ReachedAt >= convert(date, getdate() - 7) and
cs.ReachedAt < convert(date, getdate())
I'm trying to figure out how to return daily results throughout the week while keeping Friday's results visible over the weekend. My current code isn't doing the trick - do I have to mess with arrays?
SELECT ROUND(COUNT(ClosedDate) / 10, 0) * 10 [Previous Day Sales]
FROM PartsSales
WHERE (MONTH(ClosedDate) = MONTH(GETDATE())) AND (YEAR(ClosedDate) = YEAR(GETDATE())) AND (DAY(ClosedDate) = **case** DAY(GETDATE())
when 1 then 1
when 2 then 2
when 3 then 3
when 4 then 4
when 5 then 5
when 6 then 5
when 7 then 5 end
You can use datepart to find the current day of the week (1 = Sunday, 2 = Monday, etc), and then you can use dateadd to go back to Friday's date. If you convert getdate() to a date, then you'll always have the "midnight" at the begining of that day.
dateadd(
day,
case datepart(dw, getdate())
when 1 then -- Sunday
-2
when 7 then -- Saturday
-1
else -- Any other day
0
end,
convert(date, getdate())
)
Is your ClosedDate a datetime datatype? You can make much better use of your indexes by checking for a date range, rather than pulling the dateparts out (using year/month/day). Below is an example with a lot of resused code in the where clause. Of course, if it is just a date datatype, you don't even need a range, since you are calculating the date.
Below is an example. It would be better if you used variables (if you're building this into a stored proc), or perhaps a derived/CTE table. I've kept things verbose for clarity.
SELECT
ROUND(COUNT(ClosedDate) / 10, 0) * 10 [Previous Day Sales]
FROM
PartsSales
WHERE
ClosedDate between
-- Today/Friday's date:
dateadd(
day,
case datepart(dw, getdate())
when 1 then -- Sunday
-2
when 7 then -- Saturday
-1
else -- Any other day
0
end,
convert(date, getdate())
)
and
-- Add 1 day to the "Today/Friday" date.
-- This is the same logic as above, except wrapped in an extra dateadd to add 1 day to it.
dateadd(
day,
1,
dateadd(
day,
case datepart(dw, getdate())
when 1 then -- Sunday
-2
when 7 then -- Saturday
-1
else -- Any other day
0
end,
convert(date, getdate())
)
)
SELECT
DATEADD(WEEK, DATEDIFF(WEEK, 0,DATEADD(DAY, 6 - DATEPART(DAY, GETDATE()),
GETDATE())), 0),'First Monday of Current Month'
This query returns the first Monday of the current month. I just want to know how this query is working and how do I get the first Wednesday of the current month instead.
Let's simplify the whole:
SELECT DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, GETDATE()), GETDATE())), 0)
you can think at it in this way:
-- number of day to go back to reach 1st week of month
z = 6 - DATEPART(DAY, GETDATE())
-- a day in 1st week of the month
y = DATEADD(DAY, z, GETDATE())
-- number of weeks between 0 (1900-01-01) and 1st week of month
x = DATEDIFF(WEEK, 0, y)
-- add the number of weeks elapsed to 1900-01-01 to get the datetime of 1st day of 1st week of month
SELECT DATEADD(WEEK, x, 0)
I just want to know how this query is working
SELECT DATENAME(WEEKDAY, '1900-01-01') --'Monday'
SELECT DATENAME(WEEKDAY, 0) --also 'Monday'
January 1, 1900 was a Monday. When you add a week to a Monday you get another Monday. If you add 6,170 weeks to a Monday- you still get a Monday. As of 04/10/2018 there have been 6,170 weeks since Jan 1, 1900.
Simply, the query calculates these weeks, adding 6,170 weeks since Monday, Jan 1 1900.
how do I get the first Wednesday of the current month instead?
SELECT DATENAME(WEEKDAY, '1900-01-03') --Wednesday
SELECT DATENAME(WEEKDAY, 2) --also Wednesday
January 3, 1900 is represented by integer 2. We changed a 0 (which was monday) to a 2 (wednesday). If you add 6,170 weeks to a Wednesday you still get a Wednesday.
In the query there is a 0. We can change this to a 2:
SELECT
DATEADD(WEEK, DATEDIFF(WEEK, 0,DATEADD(DAY, 6 - DATEPART(DAY, GETDATE()),
GETDATE())), 2),'First Wednesday of Current Month'
Edit: I can make this perhaps a little clearer by getting rid of the integer and putting in the actual date:
SELECT
DATEADD(WEEK, DATEDIFF(WEEK, 0,DATEADD(DAY, 6 - DATEPART(DAY, GETDATE()),
GETDATE())), '1900-01-03'),'First Wednesday of Current Month'
This will give you the first Wednesday of the month, as requested in your comment:
SELECT
DATEADD(WEEK, DATEDIFF(WEEK, 0,DATEADD(DAY, 6 - DATEPART(DAY, GETDATE()), GETDATE())), 2)
AS 'First Wednesday of Current Month'
I changed your last 0 in the DATEADD function to a 2, as this is a zero based representation of the days of the week, 0 being Monday.
You can use the "Try it yourself" functionality on W3schools to play around with queries and figure them out: W3schools. Just paste your query in the console.
I tried many formula to get first Wednesday of the current month including above scripts but they were not working right if the month starts with Wednesday. (2022-06-01 can be tried). So I succeed it with my own way, you can see the sql below.
select
FirstWednesdayofCurrentMonth =
case when datepart(dw,EOMONTH(dateadd(month,-1,getdate()))) = 3
then dateadd(dd,1,EOMONTH(dateadd(month,-1,getdate()))) -- if month start with wednesday
else convert(date, DATEADD(DAY,(DATEDIFF(DAY,2,DATEADD(d,1,EOMONTH(dateadd(month,-1,getdate())))) / 7) * 7 + 7, 2)) -- if not
end
I have this query which is currently pulling all incidents from a SQL Server database which were closed on prior weekday. It's pretty neat as it's using modulo to be independent over ##DATEFIRST local settings. However I need to amend it in a way that on Mondays it gives me all incidents closed between Friday and Sunday.
Current code:
SELECT
inc.Incident_Id, inc.Close_Time
FROM
inc
WHERE
CONVERT (CHAR(10), inc.Close_Time, 126) = CONVERT (CHAR(10), (DATEADD(DAY, CASE (DATEPART(WEEKDAY, GETDATE()) + ##DATEFIRST) % 7
WHEN 1 THEN -2
WHEN 2 THEN -3
ELSE -1
END, DATEDIFF(DAY, 0, GETDATE()))),126)
I tried using another CASE DATEPART(WEEKDAY, GETDATE()) before first CONVERT but it didn't work out as it looked like >= and <= (or BETWEEN & AND) are not allowed both in one condition after THEN.
To sum up it should be like this:
On Mondays - incidents closed between Friday and Sunday
On other weekdays - incidents closed on prior day
Try this:
DECLARE #from date =
DATEADD(day,
CASE datediff(d, 0, getdate()) %7
WHEN 0 THEN -3
WHEN 6 THEN -2
ELSE -1
END, getdate())
SELECT inc.Incident_Id, inc.Close_Time
FROM inc
WHERE
inc.Close_Time >= #from
AND inc.Close_Time < #to
Monday and Sunday will return friday and after
Tuesday to saturday will return the day before.
I'm hoping to find a solution for this to automate a report I have. Basically what I'm trying to accomplish here is grabbing a date (first day of previous month, two years ago through last day of previous month current year).
So the date span if running this month would look like this: between 4/1/2013 and 3/31/2015
I have found code to get the date two years ago but I'm not able to also incorporate the month functions... Any help is very much appreciated!
For year I'm using this:
SELECT CONVERT(VARCHAR(25),DATEADD(year,-2,GETDATE()),101)
First day of previous month 2 years ago:
SELECT CONVERT(DATE,dateadd(day, -1, dateadd(day, 1 - day(GETDATE()), GETDATE())))
Last day of last month:
SELECT CONVERT(DATE,DATEADD(month, DATEDIFF(month, 0, DATEADD(year,-2,GETDATE())), 0))
Then just do whatever logic you need with them
Your where clause can look something like this:
where date >= cast(dateadd(year, -2,
dateadd(month, -1, getdate() - day(getdate()) + 1)
) as date) and
date < cast(getdate() - day(getdate()) + 1 as date)
This makes use of the handy convenience that subtracting/adding a number to a datetime is the same as adding a date. The start date says: get the first day of the month, then subtract one month, then subtract two years. This could have been done as dateadd(month, -25, . . .), but I think separating the logic is clearer.
This gives you two dates you are looking for:
SELECT
CAST((DATEADD(yy, -2, DATEADD(d, -1 * DATEPART(dd, getdate()) + 1 , GETDATE() ))) as date) as yourTwoYearsAgoDate,
CAST((DATEADD(d, -1 * DATEPART(dd, GETDATE()), GETDATE())) as date) as yourEndOfLastMonthDate
Given a reference date (e.g. "today"),
declare #today date = '23 April 2015'
The 1st of the month is computed by subtracting 1 less than the day number of the current month:
select first_of_current_month = dateadd(day,1-day(#today),#today)
The last day of the previous month is day 0 of the current month, so to get the last day of the previous month, just subtract the current day number:
select last_of_previous_month = dateadd(day,-day(#today),#today)
Moving two years back is easy:
select two_years_back = dateadd(year,-2, #today )
Putting it all together, this should do you:
declare #today date = '23 April 2015'
select *
first_day_of_current_month = dateadd(day,1-day(#today),#today),
last_day_of_previous_month = dateadd(day, -day(#today),#today) ,
date_from = dateadd(year,-2, dateadd(day,1-day(#today),#today) ) ,
date_thru = dateadd(day, -day(#today),#today)
yielding the expected results:
first_day_of_current_month: 2015-04-01
last_day_of_previous_month: 2015-03-31
date_from : 2013-04-01
date_thru : 2015-03-31
So you should be able to say something like this:
select *
from foo t
where t.transaction_date between dateadd(year,-2, dateadd(day,1-day(#today),#today) )
and dateadd(day, -day(#today),#today)
If you have to deal with datetime values rather than date, its easier to not use between and say something like this:
declare #today date = current_timestamp -- get the current date without a time component
select *
from foo t
where t.transaction_date >= dateadd(year,-2, dateadd(day,1-day(#today),#today) )
and t.transaction_date < dateadd(year, 0, dateadd(day, -day(#today),#today)
[superfluous addition of 0 years added for clarity]