SQL case expression to run report based on Fiscal Year - sql

I'm attempting to create some reports in my organization's new Help Desk system, which uses sql for its database and reporting systems. I've never interacted with sql before, so I'm having to make this up as I go. I've been able to cannibalize most of what I need from other, pre-made reports, but I'm running into a road block getting the report to group by Fiscal Year and not Calendar Year. The report I'm currently working on is meant to calculate the average time to closure on tickets. The original version grouped and ordered everything by the calendar year, and appeared to be working. However, when I try to retrofit the code to group/sort based on a July 1-June 30 Fiscal Year using the suggested method in this answer (as shown in my report code below), I'm getting the error, "The multi-part identifier "htblticket.date could not be bound."
I also tried some variants on this answer, with even less success. Does anyone have some suggestions on what I might be screwing up here, or some ideas on better ways to approach this? I'd appreciate any help/enlightenment you can provide!
Select Top 1000000 Case
When DatePart(mm, htblticket.date) > 6 Then DatePart(yyyy, htblticket.date)
+ 1
Else DatePart(yyyy, htlbticket.date)
End As [Fiscal Year],
Convert(Decimal(9,2),Avg(Cast(DateDiff(ss, htblticket.date,
ClosedDate.CloseDate) As decimal) / 86400)) As AverageDays
From htblticket
Inner Join (Select Top 1000000 htblticket.ticketid,
Max(htblhistory.date) As CloseDate
From htblticket
Inner Join htblticketstates On htblticketstates.ticketstateid =
htblticket.ticketstateid
Inner Join htblhistory On htblhistory.ticketid = htblticket.ticketid
Inner Join htblticketstates htblticketstates1 On
htblhistory.ticketstateid = htblticketstates1.ticketstateid
Inner Join htblhistorytypes On htblhistorytypes.typeid =
htblhistory.typeid
Where htblticketstates.statename = 'Closed' And
htblticketstates1.statename = 'Closed' And
htblhistorytypes.name In ('Status changed',
'Note added and state changed', 'Internal note added and state changed')
Group By htblticket.ticketid) As ClosedDate On ClosedDate.ticketid =
htblticket.ticketid
Group By Case
When DatePart(mm, htblticket.date) > 6 Then DatePart(yyyy, htblticket.date)
+ 1
Else DatePart(yyyy, htlbticket.date)
End
Order By [Fiscal Year] Desc

Your question boils down to how to group by the (Australian) financial year July-June.
One simple approach would be to create a column “financial_year_start” which is an expression that subtracts 6 months from each date and extracts the year of the result, then group by that.

Related

SQL Query to pull date range based on dates in a table

I have some SQL code which is contained in an SSRS report and when run pulls a list of student detentions for a set period such as a week or month but I have been asked to get the report to run automatically from the start of the current school term to the date the report has been run. Is this possible? We have 3 terms per year and the dates change each year. The report has multiple subscriptions which will run weekly and filter to students in particular day houses and years so we ideally need the report to update itself.
We have a table in our database titled TblSchoolManagementTermDates which includes txtStartDate and txtFinishDate columns for each term.
The date of the detention is stored in the column detPpl.dDetentionDate
The full SQL code I am currently using is:
SELECT ppl.txtSchoolID AS SchoolID,
detPpl.TblDisciplineManagerDetentionsPupilsID AS DetentionID,
ppl.txtSurname AS Surname,
ppl.txtForename AS Forename,
ppl.txtPrename AS PreferredName,
ppl.intNCYear AS Year,
ppl.txtAcademicHouse AS House,
schTermDates.intSchoolYear AS AcademicYear,
schTerms.txtName AS TermName,
CAST(schTermDates.intSchoolYear AS CHAR(4)) + '/' +
RIGHT(CAST(schTermDates.intSchoolYear + 1 AS CHAR(4)), 2) AS AcademicYearName,
detPpl.dDetentionDate AS DetentionDate,
detSessions.txtSessionName AS DetentionName,
detPpl.txtOffenceDescription AS OffenceDescription,
LEFT(Staff.Firstname, 1) + '. ' + Staff.Surname AS PutInBy,
detPpl.intPresent AS AttendedDetention
FROM dbo.TblPupilManagementPupils AS ppl
INNER JOIN
dbo.TblDisciplineManagerDetentionsPupils AS detPpl
ON detPpl.txtSchoolID = ppl.txtSchoolID
INNER JOIN
dbo.TblDisciplineManagerDetentionsSessions AS detSessions
ON detPpl.intDetentionSessionID = detSessions.TblDisciplineManagerDetentionsSessionsID
INNER JOIN
dbo.TblStaff AS Staff
ON Staff.User_Code = detPpl.txtSubmittedBy
INNER JOIN
dbo.TblSchoolManagementTermDates AS schTermDates
ON detPpl.dDetentionDate BETWEEN schTermDates.txtStartDate AND schTermDates.txtFinishDate
INNER JOIN
dbo.TblSchoolManagementTermNames AS schTerms
ON schTermDates.intTerm = schTerms.TblSchoolManagementTermNamesID
LEFT OUTER JOIN
dbo.TblDisciplineManagerDetentionsCancellations AS Cancelled
ON Cancelled.intSessionID = detPpl.intDetentionSessionID
AND Cancelled.dDetDate = detPpl.dDetentionDate
WHERE (ppl.txtAcademicHouse = 'Challoner') AND (Cancelled.TblDisciplineManagerDetentionsCancellationsID IS NULL) AND (CAST(detPpl.dDetentionDate AS DATE) >= CAST (GETDATE()-28 AS DATE))
ORDER BY ppl.txtSurname, ppl.txtForename, detPpl.dDetentionDate
What you need is to assign a couple of parameters to this code.
lets call the parameters
#term_start
and
#term_end
In your where clause you simply need to remove this piece
AND (CAST(detPpl.dDetentionDate AS DATE) >= CAST (GETDATE()-28 AS DATE))
and add this piece in
AND (CAST(detPpl.dDetentionDate AS DATE) between #term_start and #term_end
Now create another dataset based on your term dates - lets call the dataset term_dates
something like this (I'm making up these fields as I don't know what columns are available or have no sample data) Use the idea below to adapt to your requirements
select
min(term_start_date) as start_date
,max(term_end_date) as end_date
from TblSchoolManagementTermNames
where convert(date,getdate()) between term_start_date and term_end_date
Now your report should have 2 parameters.. You simply need to set the default value for the parameters.
Set the default value for #term_start as the start_date and #term_end as the end_date from your term_dates dataset
Run your report.. You should have the data between the term dates.
This should work.. unless I've misunderstood the requirement

SQL Count of Instances of Date in MS Access

So I've got a table of data in the link below (an excel spreadsheet in a zip file):
https://drive.google.com/file/d/0B4mYzBk2sry_eDg5NVhTcmtXTTg/view?usp=sharing
It has been sorted and ordered as 'Date Issued - Oldest to Most Recent'.
What I'm after is a solution that gives me a count of instances an issue was made in a month for the Financial Year (01/07/2015 to 30/06/2016).
For instance, referring to the data, July 2015 had 13 issues for that month, August 2015 had 16 issues, September 2015 had 9 issues, etc.
I've created the code below to generate the data provided in the spreadsheet:
SELECT tblCustomerNames_1.CustomerName AS [Issued To], tblCustomerNames.CustomerName AS [Issued By], tblIssueSheets.DateIssued AS [Date Issued], Count(tblMarkHistory.MarkHistoryID) AS [Marks Issued]
FROM (tblCustomerNames AS tblCustomerNames_1 INNER JOIN (tblIssueSheets INNER JOIN tblCustomerNames ON tblIssueSheets.IssuedBy = tblCustomerNames.CustomerID) ON tblCustomerNames_1.CustomerID = tblIssueSheets.CustomerID) INNER JOIN tblMarkHistory ON tblIssueSheets.IssueID = tblMarkHistory.IssueID
WHERE (((tblIssueSheets.DateIssued)>=[DateFrom] And (tblIssueSheets.DateIssued)<[DateTo]) AND ((tblCustomerNames.CustomerID)=2447))
GROUP BY tblCustomerNames_1.CustomerName, tblCustomerNames.CustomerName, tblIssueSheets.DateIssued
ORDER BY tblCustomerNames_1.CustomerName;
Please note that data has been deliberately left out as I figured the information I've provided thus far is more important to what is needed. If it isn't sufficient, let me know and I'll provide the rest of the data.
A result table of the totals of Date Issued by month and year is what I'm after.
I'm not sure how to go about this at all. Any assistance would be appreciated.
Using your saved query as source, it could be:
Select
Month([Date Issued]) As [Month],
Count(*) As Issues
From
YourQuery
Where
[Date Issued] Between #2015/07/01# And #2016/06/30#
Group By
Month([Date Issued])
Order By
Year([Date Issued]),
Month([Date Issued])
If Month# should follow the financial year, use a generic function to offset the dates from the calendar year:
Public Function DateFinancial( _
ByVal datDate As Date) _
As Date
' Number of months from start of calendar year to start of financial year.
Const clngMonthOffset As Long = 6
Dim datFinancial As Date
datFinancial = DateAdd("m", -clngMonthOffset, datDate)
DateFinancial = datFinancial
End Function

SQL Difference Between Current Year and Last Year. If Last Year Data Does Not Exist Include Current Year

In a previous post I got help finding incremental sales. The query works great. I added the breakout by product. The issue I’m having is that I need to show new products being sold. If the product did not exist last year, but we are selling it this year; then it should show up in the data table.
I tried use a CASE statement in the WHERE, but it was causing a lot of duplication of the data. I was thinking something like what is below. How do I go about including items that are only in the current year? Thank you for your help, its greatly appreciated.
Not Working Where Clause
WHERE
Ym.Project =
CASE
WHEN ymprev.Project IS NULL THEN ym.Project
ELSE ymprev.Project
END
Below is the working query.
WITH ym as(
SELECT
Product
,SUM(Sales) AS Sales
,MONTH(Date) AS Month
,YEAR(Date) AS Year
FROM SalesTable
GROUP BY
YEAR(Date)
,MONTH(Date)
,Product
)
SELECT
ymprev.Project AS PrevProject
,ym.Product
,ym.Sales
,ymprev.Sales AS PreviousSales
,(ym.Sales - ymprev.Sales) AS IncrementalSales
,ymprev.Month AS PreviousMonth
,ymprev.Year AS PreviousYear
,ym.Month
,ym.Year
FROM ym
JOIN ym ymprev on
ymprev.Year = ym.Year
AND ymprev.Month = ym.Month
AND ymprev.Product = ym.Product
ORDER BY
ym.Year
,ym.Month
Your query is implicitly using an INNER JOIN - this means that you will only see values that have a match in both datasets, just as you describe.
Try changing your FROM clause to
FROM ym
LEFT JOIN
ym ymprev on
ymprev.Year = ym.Year
AND ymprev.Month = ym.Month
AND ymprev.Product = ym.Product
You will also need to incorporate similar logic in any values that include data elements from the previous year's query. For example, ,(ym.Sales - ymprev.Sales) AS IncrementalSales will need to be turned into ,(ym.Sales - ISNULL(ymprev.Sales,0)) AS IncrementalSales or it will return NULL for any records that only exist in the current year.
Your posted query doesn't include the Project field in your CTE, so I can't tell exactly how that works, but the posted data should get you started.

Query to find late parts shipped and list them by quarter

I am having the toughest time getting a query created for this scenario:
I need to list all parts that were late and sum them by quarter.
Here is the query that I have so far:
SELECT DISTINCTROW
Format$([InvoiceSub].[Date_Shipped],'\Qq yyyy') AS [Date_Shipped By Quarter]
, Sum(InvoiceSub.Quantity) AS [Sum Of Quantity]
FROM
InvoiceSub
INNER JOIN
Job_Book_Sub
ON (InvoiceSub.[Job #] = Job_Book_Sub.[Job #])
AND (InvoiceSub.[LineItem#] = Job_Book_Sub.[LineItem#])
GROUP BY
Format$([InvoiceSub].[Date_Shipped],'\Qq yyyy')
, Year([InvoiceSub].[Date_Shipped]) * 4 + DatePart('q', [InvoiceSub].[Date_Shipped]) -1;
This query works fine to display all parts that were shipped by quarter. However, I want to see all parts that were shipped LATE by quarter.
I have a field in a table named: Job_Book_Sub, and that field is [LineItem_DueDate]. I want to use that in the query so that it will display all parts that were late ([date_shipped] > [lineitem_duedate]). The [date_shipped] is the actual date the parts were shipped, [lineitem_duedate] is the date the parts were due. I know that I need to incorporate these two fields into the query, I just don't know how.
Can someone please show me how to do this? If I add the [LineItem_DueDate] to the query, then it no longer sums the dates by quarter but instead lists every record (so instead of having 45 records with different quarters over the past 10+ years, I all of a sudden have 13000+ records because it is listing each record with date_shipped in the query.
Can someone help please?
Try adding your filter in a WHERE clause:
SELECT DISTINCTROW
Format$([InvoiceSub].[Date_Shipped],'\Qq yyyy') AS [Date_Shipped By Quarter]
, Sum(InvoiceSub.Quantity) AS [Sum Of Quantity]
FROM
InvoiceSub
INNER JOIN
Job_Book_Sub
ON (InvoiceSub.[Job #] = Job_Book_Sub.[Job #])
AND (InvoiceSub.[LineItem#] = Job_Book_Sub.[LineItem#])
WHERE
Job_Book_Sub.[date_shipped] > Job_Book_Sub.[lineitem_duedate]
GROUP BY
Format$([InvoiceSub].[Date_Shipped],'\Qq yyyy')
, Year([InvoiceSub].[Date_Shipped]) * 4 + DatePart('q', [InvoiceSub].[Date_Shipped]) -1;

Optimizing NOT IN query in Access SQL

I am new to Access and am using Access 2007.
I am doing a simple query on a database that has a list of customers who visits a workshop.
I want to send out reminders to the customers for their servicing 3 months from the last time they visited. I have created a query to be able to return me the list of customers who has visited 3 months from the current month. For example, if it is May now, 3 months ago would be March (inclusive of May).
However, customers who visited 3 months ago may have visited again 2 months ago. For example, customer A came in March and April. His last visit was in April, and hence, should not appear in the result if I were to run the query in May, as his reminder should only be sent out in June.
My query has taken care of this, however, it is rather slow. It takes some time for it to load in Access. Any help would be appreciated in optimizing it.
The only important field here is Invoice.DebCode which is the customersID in the database. There is another table DEBTOR, which is the table of customers together with their particulars.
I used the INNER JOIN as I need to display the customer(Debtor) address and particulars in the result.
SELECT Invoice.InvNo, Invoice.InvDate, Invoice.DebCode, Debtor.DebName, Debtor.AddL1, Debtor.AddL2, Debtor.AddL3, Invoice.CarNo, Invoice.ChaNo, Invoice.ExcReason
FROM Debtor
JOIN Invoice ON Debtor.DebCode = Invoice.DebCode
WHERE Year(InvDate) = Year(Now())
AND Month(InvDate) = Month(Now()) - 2
AND Invoice.DebCode NOT IN (SELECT Invoice.DebCode
FROM Invoice
WHERE Year(InvDate) = Year(Now())
AND ( (Month(InvDate) = Month(Now()) -1)
OR (Month(InvDate) = Month(Now())) )
You can dramatically speed up your query by adjusting your WHERE clauses so that the comparisons get done directly against the date field (ie, without passing it through the Month() and Year() functions). Doing it this way allows the Jet engine to make use of the index you have on the Invoice.InvDate field (you do have that field indexed, right?).
SELECT I.InvNo, I.InvDate, I.DebCode, D.DebName, D.AddL1, D.AddL2, D.AddL3,
I.CarNo, I.ChaNo, I.ExcReason
FROM Debtor AS D
INNER JOIN Invoice AS I
ON D.DebCode = I.DebCode
WHERE I.InvDate Between DateSerial(Year(Now()), Month(Now()) - 2, 1)
And DateSerial(Year(Now()), Month(Now()) - 1, 0)
AND I.DebCode NOT IN
(SELECT Invoice.DebCode FROM Invoice
WHERE Invoice.InvDate > DateSerial(Year(Now()), Month(Now()) - 1, 0))
What about something like:
SELECT a.debcode, a.debname, a.debstuff, b.most_recent AS last_over_three_months
FROM debtor AS a INNER JOIN
(
SELECT debcode, Max(invdate) AS most_recent
FROM invoice
GROUP BY debcode
)
as b
ON a.debcode= b.debcode
WHERE (month(now()) - Month(most_recent) >2);
You will have to tweak for your stuff, but the idea is a subquery that select the most recent customer visit and then selects from that only the records that meet your month criteria.
I managed to speed up the query thanks to mwolfe02 suggestion.
For archiving and completion sake, I will explain my sql statements below.
SELECT I.InvNo, I.InvDate, I.DebCode, D.DebName, D.AddL1, D.AddL2, D.AddL3,
I.CarNo, I.ChaNo, I.ExcReason
FROM Debtor AS D
INNER JOIN Invoice AS I
ON D.DebCode = I.DebCode
WHERE I.InvDate Between DateSerial(Year(Now()), Month(Now()) - 2, 1)
And DateSerial(Year(Now()), Month(Now())- 1, 0)
AND I.DebCode NOT IN
(SELECT Invoice.DebCode FROM Invoice
WHERE Invoice.InvDate Between DateSerial(Year(Now()), Month(Now()) - 1, 1)
And DateSerial(Year(Now()), Month(Now()), 0))
I edited the bottom sub query as mwolfe checked only for customers in the current month. The customers eligible for a reminder only if they came 3 months ago. That is to say, they cannot have visited between the current month and the month before.
For example, customer A visited in April and May. The current month is June, thus he is not eligible for the reminder, as his last visit was in May.
Customer B visited in April and June, thus he is not eligible for the reminder, as his last visit was in June.
Hence, the english version of the query would be:
Get customers who came 3 months ago from the last day of the current month and did not come in the current month and the month before.
I hope this helps anyone who has the same problem.
As darkjh and mikey suggested, we can "select the most recent customer visit and then selects from that only the records that meet your month criteria."
Thanks all!