SELECT previous year data based on year column - select

I am trying to develop a SSRS report. I have taken sales values from a transactional table and grouped them by year, month and subcategory. I have created this as a view. Here is the view code:
SELECT b.FiscalYear AS Year,
b.FiscalMonth AS Month,
a.SubCategoryKey,
MAX(a.SubCategoryDesc) AS SubCategoryDesc,
SUM(CAST(a.Cost + a.FreightCost AS decimal(18, 2))) AS TotalCost,
SUM(CAST(a.SalesAmount AS decimal(18, 2))) AS TotalSales,
SUM(CAST(a.Weight AS decimal(18, 2))) AS Pounds, SUM(CAST(a.SalesAmount -(a.Cost + a.FreightCost) AS decimal(18, 2))) AS Margin
FROM dbo.GrossMargin_CorrectedCosts AS a
LEFT OUTER JOIN dbo.M_DateDim AS b ON a.InvoiceDate = b.Date
GROUP BY b.FiscalYear, b.FiscalMonth, a.SubCategoryKey
I am then left with a clean view that looks like this:
SELECT [Year]
,[Month]
,[SubCategoryKey]
,[SubCategoryDesc]
,[TotalCost]
,[TotalSales]
,[Pounds]
,[Margin]
FROM [FinancialData].[dbo].[SubCategorySalesbyMonth_V]\
I'd now like to add additonal columns to this query. I am going to run this as a SSRS report and pass both Year and Month parameters. What I'd like to do is when 2017 is selected as the passed Year parameters, then I would like to show previous year values for TotalSales, TotalCost and Pounds.
Whereas the query would look something like this:
SELECT [Year]
,[Month]
,[SubCategoryKey]
,[SubCategoryDesc]
,[TotalCost]
,[TotalSales]
,[Pounds]
,[Margin]
,PreviousYearTotalSales
,PreviousYearTotalCost
,PreviousYearPounds
FROM [FinancialData].[dbo].[SubCategorySalesbyMonth_V]
Essentially when a year and month is passed to the report, I'd like to show the previous years totalsales, totalcost and pounds for that given period minus one year. I am having a heck of time doing this.
I feel like I have tried everything but obviously not. Please help.

Something that could work now that you have a view to work with is the following query.
SELECT CY.[Year]
,CY.[Month]
,CY.[SubCategoryKey]
,CY.[SubCategoryDesc]
,CY.[TotalCost]
,CY.[TotalSales]
,CY.[Pounds]
,CY.[Margin]
,PY.[TotalSales] AS PreviousYearTotalSales
,PY.[TotalCost] AS PreviousYearTotalCost
,PY.[Pounds] AS PreviousYearPounds
FROM [FinancialData].[dbo].[SubCategorySalesbyMonth_V] CY LEFT JOIN [FinancialData].[dbo].[SubCategorySalesbyMonth_V] PY
ON CY.[Year] - 1 = PY.[Year]
AND CY.[Month] = PY.[Month]
AND CY.[SubCategoryKey] = PY.[SubCategoryKey]
AND CY.[Month] = PY.[Month]
This is just a simple self left join to the same view, but the year from the current year (CY) is joining back to the prior year (PY), year - 1. It is a LEFT JOIN, so the prior year's values will be null if there isn't any prior year/month's data available.
Hope this helps.

Related

Qlik -create calculated Dimension to show barchart from month start untill filter date(5-4-4 calendar)

I have a master calendar(5-4-4) that look like the below image.
I have a Date column in the Sales table. I'm using that Date column in the Selection pane(Filter).
Example Scenario:
now I selected Selection pane date '15-10-2020', bar-chart only showing '15-10-2020' sales only. but I need to look up the master calendar and show the dimension from the start_month date to until selected date
Expected Output: bar chart needs to show dimensions from
'28-09-2020' to '15-10-2020'
The approach is to generate dates from Start_Month to TransDate (made up field name)
Lets say that this is the data we have:
Transactions:
Load * inline [
TransDate , Value
15-10-2020, 100
27-07-2021, 50
];
MasterCalendar_Temp:
Load * inline [
Start_Month, End_Month , Month_number
28-09-2020 , 01-11-2020, 1
02-11-2020 , 29-11-2020, 2
30-11-2020 , 27-12-2020, 3
28-12-2020 , 31-01-2021, 4
01-02-2021 , 28-02-2021, 5
01-03-2021 , 28-03-2021, 6
29-03-2021 , 02-05-2021, 7
03-05-2021 , 30-05-2021, 8
31-05-2021 , 27-06-2021, 9
28-06-2021 , 01-08-2021, 10
02-08-2021 , 29-08-2021, 11
30-08-2021 , 26-09-2021, 12
];
The first step is to find in which interval each TransDate is part of. For this we'll use IntervalMatch function
Inner Join
IntervalMatch ( TransDate )
Load
Start_Month,
End_Month
Resident
MasterCalendar_Temp
;
At this point MasterCalendar_Temp table will look like:
So we now know the perid for each TransDate
The next step is to load the MasterCalendar_Temp data into a separate table but concatenate Start_Month and TransDate into one:
NoConcatenate
MasterCalendar:
Load
Start_Month,
End_Month,
Start_Month & '_' & TransDate as Start_TransDate_Temp
Resident MasterCalendar_Temp;
// we dont need this table anymore
Drop Table MasterCalendar_Temp;
Once we have it we can start creating our dates
// loop through each value in Start_TransDate_Temp field
// for each step extract Start_Month and TransDate values
// use these two values to generate the dates between them
for i = 1 to FieldValueCount('Start_TransDate_Temp')
let value = FieldValue('Start_TransDate_Temp', $(i));
let startDate = num(SubField('$(value)', '_', 1));
let transDate = num(SubField('$(value)', '_', 2));
Dates:
LOAD
date('$(transDate)', 'DD-MM-YYYY') as TransDate,
date($(startDate) + IterNo() - 1, 'DD-MM-YYYY') AS DisplayDates
AUTOGENERATE (1)
WHILE
$(startDate) + IterNo() -1 <= $(transDate)
;
next
// we dont need this table anymore
Drop Table MasterCalendar;
And thats it!
After the script is reloaded we'll have two tables:
Transactions table is untouched and Dates table will have values like this:
As you can see for each TransDate we have the range of dates (from the correcponding Start_Month to TransDate
If we construct a simple bar chart (with DisplayDates as dimension and sum(Value) as measure) and do not apply any selections:
And if select one TransDate:

MDX combine two measures with different timelines on one timeline

I have two measures Time_Quantity and Time2_Quality. These measures refer to two different timelines, Time and Time2. But as an output I want to have a query which shows me the two measures on one single timeline. I guess I have to combine the time and time2 timeline, but how? This is what i have so far:
WITH
MEMBER [Time_Quantity] AS
(
[Measures].[Quantity]
,[Measures].[Time-Quantity]
,[Time Dates].[Time Year-Quarter-Month-Date]
)
MEMBER [Time2_Quality] AS
(
[Measures].[Quality]
,[Measures].[Time2-Quantity]
,[Time2 Dates].[Time2 Year-Quarter-Month-Date]
)
SELECT
{
Time_Quantity
,Time2_Quality
} ON COLUMNS
,Timeline ON ROWS // **<<how to combine Time and Time2 Timeline in one single Timeline?**
FROM [Cube];
I think that you are looking for something like below. It might need extra development because it took 13sec.
WITH MEMBER [Measures].[link_sales] AS
(LinkMember([Date].[Calendar].CURRENTMEMBER, [Ship Date].[Calendar]),[Measures].[Internet Sales Amount])
MEMBER [Measures].[link_tax] AS
(LinkMember([Date].[Calendar].CURRENTMEMBER, [Ship Date].[Calendar]),[Measures].[Internet Tax Amount])
SELECT
{[Measures].[link_sales],[Measures].[link_tax]} ON COLUMNS,
NON EMPTY [Date].[Calendar].MEMBERS ON ROWS
FROM [Adventure Works]
Result (time: 13sec)
link_sales link_tax
All Periods 29358677.2207 2348694.2301
CY 2005 3105587.3292 248446.991
H2 CY 2005 3105587.3292 248446.991
Q3 CY 2005 1349136.7362 107930.9407
July 2005 349490.1938 27959.2159

Tableau - Calculating average where date is less than value from another data source

I am trying to calculate the average of a column in Tableau, except the problem is I am trying to use a single date value (based on filter) from another data source to only calculate the average where the exam date is <= the filtered date value from the other source.
Note: Parameters will not work for me here, since new date values are being added constantly to the set.
I have tried many different approaches, but the simplest was trying to use a calculated field that pulls in the filtered exam date from the other data source.
It successfully can pull the filtered date, but the formula does not work as expected. 2 versions of the calculation are below:
IF DATE(ATTR([Exam Date])) <= DATE(ATTR([Averages (Tableau Test Scores)].[Updated])) THEN AVG([Raw Score]) END
IF DATEDIFF('day', DATE(ATTR([Exam Date])), DATE(ATTR([Averages (Tableau Test Scores)].[Updated]))) > 1 THEN AVG([Raw Score]) END
Basically, I am looking for the equivalent of this in SQL Server:
SELECT AVG([Raw Score]) WHERE ExamDate <= (Filtered Exam Date)
Below a workbook that shows an example of what I am trying to accomplish. Currently it returns all blanks, likely due to the many-to-one comparison I am trying to use in my calculation.
Any feedback is greatly appreciated!
Tableau Test Exam Workbook
I was able to solve this by using Custom SQL to join the tables together and calculate the average based on my conditions, to get the column results I wanted.
Would still be great to have this ability directly in Tableau, but whatever gets the job done.
Edit:
SELECT
[AcademicYear]
,[Discipline]
--Get the number of student takers
,COUNT([Id]) AS [Students (N)]
--Get the average of the Raw Score
,CAST(AVG(RawScore) AS DECIMAL(10,2)) AS [School Mean]
--Get the number of failures based on an "adjusted score" column
,COUNT([AdjustedScore] < 70 THEN 1 END) AS [School Failures]
--This is the column used as the cutoff point for including scores
,[Average_Update].[Updated]
FROM [dbo].[Average] [Average]
FULL OUTER JOIN [dbo].[Average_Update] [Average_Update] ON ([Average_Update].[Id] = [Average].UpdateDateId)
--The meat of joining data for accurate calculations
FULL OUTER JOIN (
SELECT DISTINCT S.[Id], S.[LastName], S.[FirstName], S.[ExamDate], S.[RawScoreStandard], S.[RawScorePercent], S.[AdjustedScore], S.[Subject], P.[Id] AS PeriodId
FROM [StudentScore] S
FULL OUTER JOIN
(
--Get only the 1st attempt
SELECT DISTINCT [NBOMEId], S2.[Subject], MIN([ExamDate]) AS ExamDate
FROM [StudentScore] S2
GROUP BY [NBOMEId],S2.[Subject]
) B
ON S.[NBOMEId] = B.[NBOMEId] AND S.[Subject] = B.[Subject] AND S.[ExamDate] = B.[ExamDate]
--Group in "Exam Periods" based on the list of periods w/ start & end dates in another table.
FULL OUTER JOIN [ExamPeriod] P
ON S.[ExamDate] = P.PeriodStart AND S.[ExamDate] <= P.PeriodEnd
WHERE S.[Subject] = B.[Subject]
GROUP BY P.[Id], S.[Subject], S.[ExamDate], S.[RawScoreStandard], S.[RawScorePercent], S.[AdjustedScore], S.[NBOMEId], S.[NBOMELastName], S.[NBOMEFirstName], S.[SecondYrTake]) [StudentScore]
ON
([StudentScore].PeriodId = [Average_Update].ExamPeriodId
AND [StudentScore].Subject = [Average].Subject
AND [StudentScore].[ExamDate] <= [Average_Update].[Updated])
--End meat
--Joins to pull in relevant data for normalized tables
FULL OUTER JOIN [dbo].[Student] [Student] ON ([StudentScore].[NBOMEId] = [Student].[NBOMEId])
INNER JOIN [dbo].[ExamPeriod] [ExamPeriod] ON ([Average_Update].ExamPeriodId = [ExamPeriod].[Id])
INNER JOIN [dbo].[AcademicYear] [AcademicYear] ON ([ExamPeriod].[AcademicYearId] = [AcademicYear].[Id])
--This will pull only the latest update entry for every academic year.
WHERE [Updated] IN (
SELECT DISTINCT MAX([Updated]) AS MaxDate
FROM [Average_Update]
GROUP BY[ExamPeriodId])
GROUP BY [AcademicYear].[AcademicYearText], [Average].[Subject], [Average_Update].[Updated],
ORDER BY [AcademicYear].[AcademicYearText], [Average_Update].[Updated], [Average].[Subject]
I couldn't download your file to test with your data, but try reversing the order of taking the average ie
average(IF DATE(ATTR([Exam Date])) <= DATE(ATTR([Averages (Tableau Test Scores)].[Updated]) then [Raw Score]) END)
as written, I believe you'll be averaging the data before returning it from the if statement, whereas you want to return the data, then average it.

Adding Columns heading to report if no data in a given period

I have a dataset for each record it has a CompanyID, RevenueMonth, RevenueYear, Revenue
When I create the report, I am grouping each CompanyID and showing their monthly revenue for a given year.
But in a given year, not all companies have any revenues for a particular month.
Example:
A sample record would look like:
CompanyID, RevenueMonth, RevenueYear, Revenue
1,05,2013,5.00
1,08,2013,6.00
1,03,2013,3.00
End Result, I would like my report to look like this with CompanyID 1.
Company ID|01|02|03|04|05|06|07|08|09|10|11|12
1 0.00|0.00|3.00|0.00|5.00|0.00|0.00|6.00|0.00|0.00|0.00|0.00
In my current Report, it will only fill column headings with March (03), May (05) and August (08).
Company ID|03|05|08
1 3.00|5.00|6.00
How do I get my Report to add the missing months for the year?
I hope my questions is clear.
Database level
Since you're only returning a year at a time, you can create a calendar table and add this to your result set:
Keeping it as simple as possible, with the date table coming from a CTE:
with months as -- get required year/months
(
select RevenueYear = 2013
, RevenueMonth = 1
union all
select RevenueYear = 2013
, RevenueMonth = RevenueMonth + 1
from months
where RevenueMonth < 12
)
select CompanyID = coalesce(r.CompanyID, c.companyID)
, RevenueMonth = coalesce(r.RevenueMonth, m.RevenueMonth)
, RevenueYear = coalesce(r.RevenueYear, m.RevenueYear)
, Revenue = isnull(r.Revenue, 0.0)
from months m
cross join (select distinct CompanyID from records) c -- make sure all companies included
left join records r on m.RevenueYear = r.RevenueYear
and m.RevenueMonth = r.RevenueMonth
SQL Fiddle with demo.
This will return a year/month for each company in the result set.
In the long run it would be better to move from a CTE to a permanent calendar table in the database.
You can then implement this in the report using a matrix style tablix.
Report level
If you'd prefer to do this at the report level, you can set up a table-style tablix with 12 permanent columns, one for each month, then populate the month revenue cells with expressions like:
=Sum(IIf(Fields!RevenueMonth.Value = 2, Fields!Revenue.Value, Nothing)
For the February column.
This would work with your existing dataset without any database code changes.

Print all dates of current year on SQL Server 2008R2

The following codes will print all dates throughout the current year on SQL Server 2008R2
with x (dy, yr)
as (
select dy, year (dy) yr
from (
select getdate () - datepart (dy, getdate ()) + 1 dy
-- the first date of the current year
) tmp1
union all
select dateadd (dd, 1, dy), yr
from x
where year (dateadd (dd, 1, dy)) = yr
)
select x.dy
from x
option (maxrecursion 400)
But there are some points that I cannot understand
As far as I can see, the first date should've been printed 400 times, are all the repetitions filtered out?
when I change 400 to less than 364, the following error returns:
[Err] 42000 - [SQL Server]The statement terminated. The maximum
recursion 363 has been exhausted before statement completion.
But how does the processor know when the statement is going to complete?
What you are dealing with here is a recursive CTE. You should probably just read more about how it works.
Basically,
It obtains the first row set from the anchor part (the first SELECT, the left part of UNION ALL).
That row set becomes aliased as x in the second SELECT (the right part of UNION ALL), called the recursive part.
The recursive part produces another row set based on x, which becomes a new x at the next iteration. That is, not the combined row set of the initial x and the last result set becomes a new x, but only the last result set.
The previous step is repeated again against the new x, and the cycle goes on until either of these is true:
another iteration produces no result set;
the MAXRECURSION limit is reached.
The final result set consists of all the partial result sets obtained from both parts of the recursive CTE.
Applying the above to your particular query:
The first SELECT produces one row containing this year's 1st of January (the date), and that becomes the first x table.
For every row of x the second SELECT produces a row containing the corresponding next date if it belongs to the same year. So, the recursive part's first iteration effectively gives us the 2nd of January. According to the above description, the result set becomes a new x.
The following iteration results in the 3rd of January, the next one produces the 4th and so on.
If the MAXRECURSION option value has safely allowed us to arrive at the moment when x contains the 31st of December, then another iteration will reveal that the next day in fact belongs to a different year. That will result in an empty row set produced, which in turn will result in termination of the recursive CTE's execution.
This is not an answer this is just another way of writing your sql. Andriy M has left you with a cool answer, you should give him the credit for the right answer.
;with x (dy)
as (
select dateadd(year, datediff(year, 0, getdate()), 0) dy
union all
select dy + 1
from x
where year (dy) = year(dy+1)
)
select x.dy
from x
option (maxrecursion 400)