I'm attempting to get a count of items within a 7-day period, while still being able to report the first date of said seven day period. The closest I've come so far is
WITH w (w, n)
AS
(
SELECT MIN(CAST(CreatedDate AS DATE))
OVER(
PARTITION BY DATEPART(WEEK, CAST(CreatedDate AS DATE))
ORDER BY DATEPART(WEEK, CAST(CreatedDate AS DATE))
)
,COUNT(*)
FROM dbo.Tbl
WHERE
CreatedDate >= CAST(DATEADD(MONTH,-6,GETDATE()) AS DATE)
GROUP BY CAST(CreatedDate AS DATE)
)
SELECT w.w AS [Week of], SUM(w.n) AS [Items]
FROM w
GROUP BY w.w
ORDER BY 1 DESC
But this unfortunately does not work for the first or last week of the year, and will not work if the date range includes more than one year.
Is there a way to group by a seven day period while still being able to get the first date in said period?
You can use the following to get your weeks based on your date value. You will need to play around with the days added to get the right days of the week for your situation, but you will always be guaranteed the same start and end day. In this case the week starts on Sunday and ends on Saturday:
declare #Date datetime
set #Date = '20160802'
select #Date - DATEPART(dw, #Date) + 1 as FirstDateOfWeek
,#Date + (7 - DATEPART(dw, #Date)) as LastDateOfWeek
For a fully bulletproof solution you could add in logic to check and use ##DATEFIRST and assign the adjustment values accordingly.
Once you have your week start value, you can use that in your group by:
select CreatedDate - DATEPART(dw, #Date) + 1 as FirstDateOfWeek
,count(*)
from tbl
group by CreatedDate - DATEPART(dw, #Date) + 1
order by FirstDayOfWeek
Related
I'm wondering how could I use the GROUP BY on month, but from the n-th of a month to the (n-1)-th of the next month. For exemple, I want to GROUP by from the 20 of january to the 19 of february, from the 20 of february to the 19 of march...
Currently I can GROUP BY Months and dates <20 with a condition. There is the demo :
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=8b55b7df44350c4ad3c595d03e421b6e
But now I don't know how can I group the 'false' of a month (ie that the value is the sum for days > 20) with the 'true' of the next month.
Does anyone as a idea how to do that ?(maybe what i'm trying os not the good way how to do that)
For each date before the 20th you can subtract 1 month and group it with the previous month:
WITH cte AS (
SELECT CASE
WHEN DAY(Date) >= 20 THEN Date
ELSE DATEADD(mm, -1, Date)
END Date,
Value
FROM tablename
)
SELECT YEAR(Date) year, MONTH(Date) month, SUM(Value) val
FROM cte
GROUP BY YEAR(Date), MONTH(Date)
ORDER BY YEAR(Date), MONTH(Date)
Or you can change the CASE expression to:
CASE
WHEN DAY(Date) < 20 THEN Date
ELSE DATEADD(mm, 1, Date)
END
so that all dates after the 19th are grouped with the next month.
Or:
SELECT FORMAT(DATEADD(dd, -20, Date), 'yyyy-MM') year_month,
SUM(Value) val
FROM tablename
GROUP BY FORMAT(DATEADD(dd, -20, Date), 'yyyy-MM')
See the demo.
I need list of those employees who are absent one day before and one day after weekend in a week......like if some is absent in Friday and present on Monday should not be included in the list
Use datepart(weekday, ) to fetch all records relative to monday and friday.
Have a look at SET DATEFIRST function too.
select *
from your_table
where datepart(weekday, Date) = 5
or datepart(weekday, Date) = 1;
This will list all employee id that are absent on a Friday and the following Monday (+1 week). I set-up a calendar week from mininum date to maximum date from the table and get only Friday and Monday. Then get all empid that has no attendance in any of those dates.
with caldte as (
SELECT dateadd(day, rn - 1, t.mindte) as dates,
datepart(weekday, dateadd(day, rn - 1, t.mindte)) as weekday,
datepart(wk, dateadd(day, rn - 1, t.mindte)) as weeknum
FROM (
select row_number() OVER ( order by c.object_id ) AS rn
FROM sys.columns c) rns,
(select min(dte) as mindte, max(dte) as maxdte
from tbl) t
WHERE rn - 1 <= datediff(day, t.mindte, t.maxdte)
and datepart(weekday, dateadd(day, rn - 1, t.mindte)) in (2, 6)
)
select distinct empid
from tbl
where empid not in (
select t.empid
from caldte c, tbl t
where c.dates = t.dte)
order by empid
Good Day! I am working on a chart where I need to display all the days of the current week to show the sales per Week. So far, I am able to display all the days of the current week, I'm just having a trouble in displaying the sales for each day of the week.Since there are no records in the database for the days of the week, it the TOTAL_SALES column should all return a Null value. Instead, it returns the total sales recorded in the database. Here is my Stored Procedure query so far.
WITH DAYSOFTHEWEEK AS
(
SELECT 0 DAY
UNION ALL
SELECT DAY + 1 FROM DAYSOFTHEWEEK WHERE DAY < 6
)
SELECT DATEADD(DAY, DAY, DATEADD(DAY, 2-DATEPART(WEEKDAY, CONVERT (date, GETDATE())), CONVERT (date, GETDATE()))) AS DAY_OF_THE_WEEK,
SUM([ORDER].NET_AMOUNT) AS TOTAL_SALES
FROM DAYSOFTHEWEEK, [ORDER]
GROUP BY DAYSOFTHEWEEK.DAY
I tried adding this condition statement,
WHERE DAYSOFTHEWEEK.DAY IN ([ORDER].ORDER_DATE)
But it returns this error
Operand type clash: date is incompatible with int
Can someone help me out on this?Is there a work around with the code that I already have? Thanks in advance!
What I think you're after is a SUM of each day's sales for the current week with NULL if there are no sales. The secret is to left join your date list onto your data:
-- Setup some fake sales data
WITH TestData(N, Order_Date, Net_Amount) AS (
SELECT 1 N, CAST(GETDATE() AS DATE) Order_Date, RAND() * 100 Net_Amount
UNION ALL
SELECT N+1 N, CAST(GETDATE()-N/5 AS DATE) Order_Date, RAND(CHECKSUM(NEWID())) * 100 Net_Amount FROM TestData
WHERE N < 20
)
SELECT TestData.Order_Date, TestData.Net_Amount INTO #Order FROM TestData
--Set the first day of the week (if required)
SET DATEFIRST 7 --Sunday
;WITH Days(N,DayOfTheWeek) AS (
SELECT 1 N, DATEADD(DAY, 1-DATEPART(WEEKDAY, GETDATE()), CONVERT(DATE,GETDATE())) DayOfTheWeek
UNION ALL
SELECT N+1 N,DATEADD(DAY, 1, DayOfTheWeek) DayOfTheWeek FROM Days
WHERE N < 7
)
SELECT d.DayOfTheWeek, SUM(Net_Amount) TotalAmount
FROM Days d
LEFT JOIN #Order ON d.DayOfTheWeek = Order_Date
GROUP BY d.DayOfTheWeek
DayOfTheWeek TotalAmount
------------ ----------------------
2016-08-07 219.036784917497
2016-08-08 273.319570812461
2016-08-09 271.148114731087
2016-08-10 194.780039228967
2016-08-11 NULL
2016-08-12 NULL
2016-08-13 NULL
Here is every day this week, starting at your datefirst date, which can be temporarily varied for the query with SET DATEFIRST if you need to have some other week start date
I think you have some sales table there that you haven't shown us, you need to join to that on date, then group by
WITH DAYSOFTHEWEEK AS
(
SELECT cast(dateadd(
day,
-datepart(weekday,getdate()) + 1 ,
GETDATE()
)
as date) [DAY], 0 as cnt
UNION ALL
SELECT dateadd(day,1,[DAY]), cnt + 1 FROM DAYSOFTHEWEEK WHERE cnt < 6
)
select DAYSOFTHEWEEK.[day], SUM([ORDER].NET_AMOUNT) AS TOTAL_SALES from daysoftheweek
JOIN
SalesTable on
CAST(SalesTable.SalesDate date) = DAYSOFTHEWEEK.[day]
GROUP BY DAYSOFTHEWEEK.[day]
A little over complicated for me:
To get name of the week use, for example
SELECT DATENAME(dw,getdate())
But you really need something like this:
SELECT ProductName,Sum(Sales) From NameOfTable GROUP BY
DATENAME(ww,salesDate)
I have a SQL query to the today birthdays user.
SELECT * FROM Table_EmployeeInfo WHERE MONTH(DOB) = MONTH(getdate()) and day(DOB)=day(getdate())
But I need top 5 users SQL query for upcoming birthdays, not today users birthday, it should be tomorrow, if no body will tomorrow means take from next days.
Also, I need recent top 5 user birthdays gone, not today users birthday.
You can use ROW_NUMBER() function like this:
SELECT * FROM(
SELECT t.*,
row_number() OVER(order by datepart(mm,t.DOB),datepart(dd,t.DOB)) as rnk
FROM Table_EmployeeInfo t
where t.DOB > getdate())
WHERE rnk <= 5
Messy, but it works:
select * from (
SELECT datediff(dd,DATEADD(yyyy, DATEDIFF(yyyy, DOB, GETDATE()), DOB),getdate()) as daysSinceBD
, DENSE_RANK() OVER( ORDER BY datediff(dd,DATEADD(yyyy, DATEDIFF(yyyy, DOB, GETDATE()), DOB),getdate()) desc ) as Ranking
, * FROM Table_EmployeeInfo
where datediff(dd,DATEADD(yyyy, DATEDIFF(yyyy, DOB, GETDATE()), DOB),getdate()) < 0
)x where ranking <=5 order by 1 desc
Try this below for select upcoming 5 birthdays
SELECT top 5 * FROM Table_EmployeeInfo WHERE CONVERT(DATETIME,
CONVERT(VARCHAR(10), DOB, 111)) > CONVERT(DATETIME,
CONVERT(VARCHAR(10), GETDATE(), 111)) order by dob
This one for past 5 birthdays
SELECT top 5 * FROM Table_EmployeeInfo WHERE CONVERT(DATETIME,
CONVERT(VARCHAR(10), DOB, 111)) < CONVERT(DATETIME,
CONVERT(VARCHAR(10), GETDATE(), 111)) order by dob
I have tested the codes against an old database with Date of Birth data, and they worked.
Since I do not have Date of Birth exact time (hour and etc), I did not handle the situation where I have 10 or more people with birthday on same day.
I have used the "OFFSET - FETCH" approach, that can help you if you need some kind of pagination in the future.
I do not know your scenario and/or data model, but if this query is to be run frequently and database tends to be big, I'd suggest you to consider a computed column "Anniversary", or anything compliant with your database names convention. This is because you have to use a lot of functions to achieve what you need, and this is not good for performance. With a computed column, you have less complexity on queries and a better indexing strategy.
Please find below the code I have written, and let me know if it helps.
-- LAST 5 BIRTHDAYS GONE, NOT FROM TODAY
SELECT
*
FROM
Table_EmployeeInfo AS T
WHERE
DATEPART(DD, T.DOB) < DATEPART(DD, GETDATE())
AND
DATEPART(MM, T.DOB) <= DATEPART(MM, GETDATE())
ORDER BY
DATEPART(MM, T.DOB) DESC
,DATEPART(DD, T.DOB) DESC
OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY
-- NEXT 5 BIRTHDAYS TO COME, NOT FROM TODAY
SELECT
*
FROM
Table_EmployeeInfo AS T
WHERE
DATEPART(DD, T.DOB) > DATEPART(DD, GETDATE())
AND
DATEPART(MM, T.DOB) >= DATEPART(MM, GETDATE())
ORDER BY
DATEPART(MM, T.DOB) ASC
,DATEPART(DD, T.DOB) ASC
OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY
P.S.: OFFSET-FETCH will work only on SQL Server 2012 or above. If you are using and older version, try using TOP(5) instead.
I have tried searching for a soln to this but have been unable yet to find one.
I need to select the last 4 weeks of data from todays date which is not the issue as I just do a date >= Dateadd(mm, -1, getdate()). However I also want the same 4 weeks of data from the previous year. But I dont want (for example) June 1-30 2010 and June 1-30 2011, I would need
June 30th (thursday) 2011 and 4 weeks prior AND July 1st and four weeks prior as july 1st was the thursday in the same week from the prev year.
so 8 weeks of data would be returned.
Thanks for the help!
You can use some more DATEADD() goodness to go back to the previous year:
where theDate >= DATEADD(mm, -1, GETDATE())
OR
(theDate <= DATEADD(week,-52,convert(datetime,GETDATE()))
and
theDate >= DATEADD(mm,-1,DATEADD(week,-52,convert(datetime,GETDATE()))))
Subtracting 52 weeks from 6/30/2011 returns 7/1/2010 as you requested... Then using your original subtraction of 1 month from there for the lower bound.
You could also switch the whole thing to use weeks...
where theDate >= DATEADD(week, -4, GETDATE())
OR
(theDate <= DATEADD(week,-52,convert(datetime,GETDATE()))
and
theDate >= DATEADD(week,-56,convert(datetime,GETDATE())))
you may do a serial of ORed BETWEEN conditions:
select
...
from
...
where
1=1
or date between Dateadd(mm, -2, getdate()) and Dateadd(mm, -1, getdate())
or date between Dateadd(mm, -11, getdate()) and Dateadd(mm, -10, getdate())
order by
date
did i understand right?
Last four weeks sales data:
with cte as (
select case when row_number() OVER (ORDER BY DATEPART(wk,CreatedDate) DESC) = 2 then 'last 1st Week'
when row_number() OVER (ORDER BY DATEPART(wk,CreatedDate) DESC) = 3 then 'last 2nd Week'
when row_number() OVER (ORDER BY DATEPART(wk,CreatedDate) DESC) = 4 then 'last 3rd Week'
when row_number() OVER (ORDER BY DATEPART(wk,CreatedDate) DESC) = 5 then 'last 4th Week'
else 'last 5th plusweek'
end as weeks,
sum(amt) as week_wise_sale,
row_number() OVER (ORDER BY DATEPART(wk,CreatedDate) DESC) AS [row number]
from samt
group by DATEPART(wk,CreatedDate)
) select * from cte where [ROW NUMBER] > 1