Concatenating Month and Year of dates to order them by - sql

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);

Related

How to select the past three months data from a date parameter

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)

SELECT SQL data based on date, fill month

I'm selecting year, month and net sales from invoice table. The problem is that if there's no data under specific month, there will be no rows for that month. Can you help me? Net sales should be zero if there is not any data.
SELECT
DATEPART(year, date) as 'year',
DATEPART(month, date) as 'month',
SUM(netsales) as netsales
FROM invoice
WHERE
date >= '2015-01-01'
AND date <= '2016-12-31'
GROUP BY
DATEPART(year, date),
DATEPART(month, date)
Thanks in advance.
You need a calendar table and left join
;with calendar as
(
select cast('2015-01-01' as date) as dates -- start date
union all
select dateadd(mm,1,dates) from cte where dates < '2016-12-31' -- end date
)
SELECT
DATEPART(year, c.dates) as 'year',
DATEPART(month, c.dates) as 'month',
SUM(netsales) as netsales
FROM calendar C left join invoice i on c.dates = cast(i.[date] as date)
GROUP BY
DATEPART(year, date),
DATEPART(month, date)
I have generates dates on the fly using Recursive CTE, but I will always suggest to create a calendar table physically and use it in such queries

Group by Weeks for successive years

I am trying to aggregate data by weeks. My sample query looks like
SET DATEFIRST 1
Select ID,
DATENAME(week, p.SellingDate) as SellingWeek,
DATENAME(year, p.SellingDate) as SellingYear,
SUM(Quantity),
SUM(Revenue)
From dbo.Sales
Where
p.SellingDate >=’2015-12-20’ and P.SellingDate < ’2016-02-27’
Group by ID,
DATENAME(week, p.SellingDate),
DATENAME(year, p.SellingDate)
When I try to do this, I am facing an issue:
The query returns correct data but the issue appears in the last week of 2015. It considers only the days (12/28 to 12/31) that are part of 2015 as the 53rd week and it considers the remaining part (01/01 to 01/03) as a new week of 2016. I only one row that has data for the whole week i.e. 12/28 to 01/03) but SQL Server returns 2 rows. Is there a workaround this?
I would think you want to create a construct for grouping on week start and week end, then using that to group by for your aggregation clauses.
SET DATEFIRST 1
Select DATEADD(dd, -(DATEPART(dw, p.SellingDate)-1), p.SellingDate) AS [WeekStart],
DATEADD(dd, 7-(DATEPART(dw, p.SellingDate)), p.SellingDate) AS [WeekEnd],
SUM(Quantity),
SUM(Revenue)
From dbo.Sales
Where
p.SellingDate >= '2015-12-20' and P.SellingDate < '2016-02-27'
Group by ID,
DATEADD(dd, -(DATEPART(dw, p.SellingDate)-1), p.SellingDate),
DATEADD(dd, 7-(DATEPART(dw, p.SellingDate)), p.SellingDate)
You can fix this by setting the week not based on the date of the sale but rather on the monday of week of the date of the sale.
select
getdate() as today
datename(week, dateadd(dd,-1* (datepart(weekday, getdate())-1),getdate())) as monday,

SQL | Adding a specific amount and outputting the sum in a seperate column

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

SQL Hurdle - SQL Server 2008

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.