The following query returns the total amount of orders, per week, for the past 12 months (for a specific customer):
SELECT DATEPART(year, orderDate) AS [year],
DATEPART(month, orderDate) AS [month],
DATEPART(wk, orderDate) AS [week],
COUNT(1) AS orderCount
FROM dbo.Orders (NOLOCK)
WHERE customerNumber = #custnum
AND orderDate >= DATEADD(month, -12, GETDATE())
GROUP BY DATEPART(year, orderDate),
DATEPART(wk, orderDate),
DATEPART(month, orderDate)
ORDER BY DATEPART(year, orderDate),
DATEPART(wk, orderDate)
This returns results like:
year month week orderCount
2008 1 1 23
2008 3 12 5
...
As you can see, only weeks that have orders for this customer will be returned in the resultset. I need it to return a row for every week in the past 12 months... if no order exists in the week then returning 0 for orderCount would be fine, but I still need the year, week, and month. I can probably do it by creating a separate table storing the weeks of the year, then left outer join against it, but would prefer not to. Perhaps there's something in SQL that can accomplish this? Can I create a query using built in functions to return all the weeks in the past 12 months with built in SQL functions? I'm on SQL Server 2008.
Edit:
Using Scott's suggestion I posted the query solving this problem below.
You could join to a recursive CTE - something like below should give you a start...
WITH MyCte AS
(SELECT MyWeek = 1
UNION ALL
SELECT MyWeek + 1
FROM MyCte
WHERE MyWeek < 53)
SELECT MyWeek,
DATEPART(year, DATEADD(wk, -MyWeek, GETDATE())),
DATEPART(month, DATEADD(wk, -MyWeek, GETDATE())),
DATEPART(wk, DATEADD(wk, -MyWeek, GETDATE()))
FROM MyCte
The table method you are already aware is the best way to go. Not only does it give you alot of control, but it is the best performing.
You could write sql code (user function) to do this, but it won't be as flexible. RDBMs are made for handling sets.
Solution using CTE: (thanks to Scott's suggestion)
;WITH MyCte AS
(SELECT MyWeek = 1
UNION ALL
SELECT MyWeek + 1
FROM MyCte
WHERE MyWeek < 53)
SELECT myc.[year],
myc.[month],
myc.[week],
isnull(t.orderCount,0) AS orderCount,
isnull(t.orderTotal,0) AS orderTotal
FROM (SELECT MyWeek,
DATEPART(year, DATEADD(wk, -MyWeek, GETDATE())) AS [year],
DATEPART(month, DATEADD(wk, -MyWeek, GETDATE())) AS [month],
DATEPART(wk, DATEADD(wk, -MyWeek, GETDATE())) AS [week]
FROM MyCte) myc
LEFT OUTER JOIN
(SELECT DATEPART(year, orderDate) AS [year],
DATEPART(month, orderDate) AS [month],
DATEPART(wk, orderDate) AS [week],
COUNT(1) AS orderCount,
SUM(orderTotal) AS orderTotal
FROM dbo.Orders (NOLOCK)
WHERE customerID = #custnum
AND orderDate >= DATEADD(month, -12, GETDATE())
GROUP BY DATEPART(year, ODR_DATE),
DATEPART(wk, orderDate),
DATEPART(month, orderDate)) t ON t.[year] = myc.[year] AND t.[week] = myc.[week]
ORDER BY myc.[year],
myc.[week]
Edit: just noticed one week is being duplicated (2 records for the same week)... probably a simple logical error... disregard... ID-10-T... apparently a week can span months... who would have known lol
In the past I've done this using the table approach that you mention, the other way was to create a table function that I could pass arguments to specifying the start and end range that I wanted and it would build results dynamically to save needing to add a table with all the data.
Related
I have an issue that I think I am overthinking. I have a SQL view that I import to PowerBI, and I want to use this to order a graph I am building. The part I am ordering on and having issues with is the date part column:
select
Ticket_ClientName, count(Ticket_ClientName) as DisplayNameCount,
concat( datepart(month, cast(t.ticket_opendate as date)),
datepart(year, cast(t.ticket_opendate as date))) as OpenDate,
datename(month, cast(t.ticket_opendate as date)) as Month_Name
from
dbo.Ticket t
where
CAST(t.ticket_opendate as date) >= '02/01/2021'
and t.Ticket_ClientName is not null
and Ticket_DisplayId not like 'EH%'
group by
ticket_ClientName,
concat( datepart(month, cast(t.ticket_opendate as date)),
datepart(year, cast(t.ticket_opendate as date))),
datename(month, cast(t.ticket_opendate as date))
The issue I am going to face is when the year rolls over, as the months of October to December will always remain as the latest month, due to them being the highest number, does anyone have a way to get around this?
I apologise in advance if I am overthinking this.
You can group and order by EOMONTH, which returns the last day of that month.
select
Ticket_ClientName,
count(Ticket_ClientName) as DisplayNameCount,
FORMAT(EOMONTH(t.ticket_opendate), 'MMyyyy') as OpenDate,
datename(month, EOMONTH(t.ticket_opendate)) as Month_Name
from
dbo.Ticket t
where
t.ticket_opendate >= '20210102'
and t.Ticket_ClientName is not null
and Ticket_DisplayId not like 'EH%'
group by
ticket_ClientName,
EOMONTH(t.ticket_opendate)
order by
EOMONTH(t.ticket_opendate);
I would like to display the name of the month along with a count for each of these months. But I only want the past 3 months from a date parameter that the user will select.
This is what I have currently:
SELECT
DATENAME(month, DateDue),
COUNT(SiteAudit.SiteAuditID) AS SiteAuditID
FROM
SiteAudit
WHERE
DateDue >= Dateadd(month, -3, #Date)
GROUP BY
DATENAME(month, DateDue)
I'm not sure where I am going wrong because now I am getting more than 3 months returned.
If anyone could please help I would greatly appreciate it.
Thank you
You should use DateDue between DateAdd(month, -3, #Date) and #Date
Your original query will select data which due date after the #Date
Complete query:
SELECT
DATENAME(month, DateDue),
COUNT(SiteAudit.SiteAuditID) AS SiteAuditID
FROM
SiteAudit
WHERE
DateDue BETWEEN Dateadd(month, -3, #Date) AND #Date
GROUP BY
DATENAME(month, DateDue)
I am trying to implement a SQL query where I need to add up a specific amount of employees hired per week, month, year and display that into a named column. So i.e. Total Hired Per Week, Total Hired Per Month, Total Hired for Year.
Two tables are joined. One is a simple table that lists every single day since 1-1-1900 to 12-31-2099. The other is a simple employee database that lists their Hire Date.
Adding explanations will assist me in learning SQL logic and will be a plus.
I am giving the query below and the output as well in the picture:
.
If I have not explained my issue clearly, please advise and I will try to restate my question.
Query:
DECLARE #STARTDATE DATETIME,
#ENDDATE DATETIME
SET #STARTDATE = '1989-12-31' -- >=
SET #ENDDATE = '2015-10-31 23:59:59' -- <
SELECT c.calendarDate,
Count(e.empid) as Number_Hired,
datepart(week,c.calendarDate) as Week,
datepart(month,c.calendarDate) as Month,
datepart(year,c.calendarDate) as Year
FROM intranet.dbo.igbl_calendar c
LEFT JOIN intranet.dbo.iemp_employee e on CONVERT(DATE, c.calendarDate) = CONVERT(DATE, e.hireDate) and aliasID = 'P'
WHERE c.calendarDate BETWEEN #STARTDATE AND #ENDDATE
GROUP BY c.calendarDate
ORDER BY c.calendarDate
Assuming all you're looking to do is add on 3 additional columns to the current query, you could left join inline views or CTEs that do the individual counts for week, month, year.
e.g.
DECLARE #STARTDATE DATETIME = '1989-12-31'
, #ENDDATE DATETIME = '2015-10-31 23:59:59'
SELECT
c.calendarDate,
Count(e.empid) as Number_Hired,
datepart(week, c.calendarDate) as [Week],
datepart(month, c.calendarDate) as [Month],
datepart(year, c.calendarDate) as [Year],
ew.WeekCount Number_Hired_This_Week,
em.MonthCount Number_Hired_This_Month,
ey.YearCount Number_Hired_This_Year
FROM
intranet.dbo.igbl_calendar c
LEFT JOIN intranet.dbo.iemp_employee e on c.calendarDate = e.hireDate AND aliasID = 'P'
LEFT JOIN (SELECT DATEPART(ww, hireDate) [Week]
, DATEPART(yy, hireDate) [Year]
, COUNT(empid) [WeekCount]
FROM intranet.dbo.iemp_employee
GROUP BY DATEPART(ww, hireDate), DATEPART(yy, hireDate)) ew
ON ew.[Week] = DATEPART(ww, c.calendarDate)
AND ew.[Year] = DATEPART(yy, c.calendarDate)
LEFT JOIN (SELECT DATEPART(mm, hireDate) [Month]
, DATEPART(yy, hireDate) [Year]
, COUNT(empid) [MonthCount]
FROM intranet.dbo.iemp_employee
GROUP BY DATEPART(mm, hireDate), DATEPART(yy, hireDate)) em
ON em.[Month] = DATEPART(mm, c.calendarDate)
AND em.[Year] = DATEPART(yy, c.calendarDate)
LEFT JOIN (SELECT DATEPART(yy, hireDate) [Year]
, COUNT(empid) [YearCount]
FROM intranet.dbo.iemp_employee
GROUP BY DATEPART(yy, hireDate)) ey
ON ey.[Year] = DATEPART(yy, c.calendarDate)
WHERE
c.calendarDate BETWEEN #STARTDATE AND #ENDDATE
GROUP BY
c.calendarDate, ew.WeekCount, em.MonthCount, ey.YearCount
ORDER BY
c.calendarDate
The basic logic being you want to find the total for a year by grouping them by the year (using datepart here), the total for each month by grouping them by year and month, and the total for each week by grouping on year and week. And then joining them back to the original query based on year/month/week.
Though honestly, I'd just do them in separate queries rather than having them all output in the one.
Personally I would minimize using JOIN when solving this kind of problem since the KEY that you need to JOIN together requires further processing, datepart() in this context.
I prefer to use a nested SELECT statement instead.
DECLARE #STARTDATE DATETIME,
#ENDDATE DATETIME
SET #STARTDATE = '1989-12-31' -- >=
SET #ENDDATE = '2015-10-31 23:59:59' -- <
WITH EmpData AS (
SELECT count(*) 'HireCount',
datepart(year, e.hireDate) 'HireYear',
datepart(month, e.hireDate) 'HireMonth',
datepart(week, e.hireDate) 'HireWeek'
FROM intranet.dbo.iemp_employee e
WHERE e.aliasID = 'P'
GROUP BY datepart(year, e.hireDate), datepart(month, e.hireDate), datepart(week, e.hireDate)
)
SELECT
datepart(year, c.calendarDate) AS Year,
datepart(month, c.calendarDate) AS Month,
datepart(week, c.calendarDate) AS Week,
(SELECT sum(HireCount) FROM EmpData WHERE datepart(year, c.calendarDate) = HireYear) 'Year Hire Count',
(SELECT sum(HireCount) FROM EmpData WHERE datepart(year, c.calendarDate) = HireYear AND datepart(month, c.calendarDate) = HireMonth) 'Month Hire Count',
(SELECT sum(HireCount) FROM EmpData WHERE datepart(year, c.calendarDate) = HireYear AND datepart(month, c.calendarDate) = HireMonth AND datepart(week, c.calendarDate) = HireWeek) 'Week Hire Count'
FROM
intranet.dbo.igbl_calendar c
WHERE
c.calendarDate BETWEEN #STARTDATE AND #ENDDATE
GROUP BY
datepart(year, c.calendarDate) as Year, datepart(month, c.calendarDate) as Month, datepart(week, c.calendarDate) as Week
ORDER BY
1,2,3
I have a simple query:-
SELECT *
FROM dbo.NGPCostPosition
That returns the below data:-
I want to try and display a total cost for just the current month while still having access to all other data so maybe a new column called current month that would only be populated by items that fall into that category?
What is the best way to do this?
All advice welcome and appreciated.
This should add an extra column to the query just showing current month total costs.
SELECT
*,
CASE
WHEN
DATEPART(MOTNH, TranDate) = DATEPART(MONTH, GETDATE()) AND
DATEPART(YEAR, TranDate) = DATEPART(YEAR, GETDATE())
THEN TotalCost
ELSE 0
END CurrentMonthCost
FROM dbo.NGPCostPosition
You could add a column to your query like so:
Select *,
CASE WHEN
datepart(mm, getdate()) == datepart(mm, TranDate)
and datepart(yy, getdate()) == datepart(yy, tranDate)
then TotalCost
else 0
end as CurrentMonthTotalCost
And then sum that up somewhere in your sheet. You could also use date formatting (I don't prefer these as they're a bit of a cipher as to what you're comparing):
Select *,
CASE
WHEN CONVERT(VARCHAR(7), GETDATE(), 111) == CONVERT(VARCHAR(7), TRANDATE, 111)
THEN TotalCost
ELSE 0
End as CurrentMonthTotalCost
try this:
SELECT doctype, projectNo, CostCat, [all other columns etc.],
(Select Sum(Quantity * UnitCost) From NGPCostPosition
Where transDate >= DateAdd(month,
datediff(month,0,p.Transdate), 0)
And transDate < DateAdd(month,
datediff(month,0, p.Transdate), 31)) MonthlyTotal
FROM NGPCostPosition p
I am trying to figure out how to compare the current day's data to the same data from a week ago, 2 weeks, etc. Let's say I have a table called "Order" with 2 columns:
Order table
-----------
OrderID int identity
OrderDate datetime
If today, is Monday, I would like to be able to compare the number of orders from today to the previous Mondays for an entire year. Is this possible with a single SQL Server query? I'm using SQL 2008 if it makes a difference.
select CAST (OrderDate as date) as [Date], COUNT(*)
from Orders
where OrderDate > DATEADD(YEAR,-1, getdate())
and DATEPART(DW,OrderDate ) = DATEPART(DW,GETDATE())
group by CAST (OrderDate as date)
Try
SELECT [ColumnsYouWant]
FROM [OrderTable]
WHERE datepart(weekday, OrderDate) = datepart(weekday, getdate())
AND OrderDate >= dateadd(yyyy, -1, getdate())
This gives you Monday order counts by week number:
select year(OrderDate) as Year,
DATEPART(WEEK, OrderDate) as Week,
COUNT(*) as MondayOrderCount
from Order
where DATEPART(WEEKDAY, OrderDate) = 2
group by year(OrderDate), DATEPART(WEEK, OrderDate)
order by Year, Week