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

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.

Related

TSQL select and join issue

I have two tables, EMPL which is a historical employee table to track changes in an employee's tax rate and PAYROLL which is also a historical table filled with employee pay over a number of periods.
FROM EMPL, based upon the EMPL.effect_pd <= PAYROLL.payroll_pd, only one record should be joined from EMPL to PAYROLL.
Below are the two tables, query and result set. However, I only want 1 record for each employee per pay period, which matches the relevant employee record based upon the payroll_pd and effect_pd.
(Click image to enlarge)
first of all - welcome!
You wrote "...FROM EMPL, based upon the EMPL.effect_pd <= PAYROLL.payroll_pd ..." but you start your SQL with PAYROLL and not with EMPL.
Pls test this statement first:
SELECT
E.rec_id
,E.empl_id
,E.empl_name
,E.tax_rate
,E.effect_pd
,P.rec_id
,P.payroll_pd
,P.empl_id
,P.pd_pay
FROM
empl AS E
LEFT OUTER JOIN
payroll AS P
ON E.empl_id = P.empl_id
AND E.effect_pd < P.payroll_pd
After that you get 7 records witch are uniqe.
i think, thats it.
Best regards
After 3 days of messing around with the code, I finally arrived at the solution which is:
SELECT * FROM PAYROLL p
LEFT JOIN EMPL e on p.empl_id = e.empl_id
WHERE e.rec_id = ( SELECT TOP 1 c.rec_id
FROM EMPL c
WHERE c.empl_id = p.empl_id
AND p.payroll_pd >= c.effect_pd
ORDER BY c.effect_pd DESC );

Filter portal for most recently created record by group

I have a portal on my "Clients" table. The related table contains the results of surveys that are updated over time. For each combination of client and category (a field in the related table), I only want the portal to display the most recently collected row.
Here is a link to a trivial example that illustrates the issue I'm trying to address. I have two tables in this example (Related on ClientID):
Clients
Table 1 Get Summary Method
The Table 1 Get Summary Method table looks like this:
Where:
MaxDate is a summary field = Maximum of Date
MaxDateGroup is a calculated field = GetSummary ( MaxDate ;
ClientIDCategory )
ShowInPortal = If ( Date = MaxDateGroup ; 1 ; 0 )
The table is sorted on ClientIDCategory
Issue 1 that I'm stumped on: .
ShowInPortal should equal 1 in row 3 (PKTable01 = 5), row 4 (PKTable01 = 6), and row 6 (PKTable01 = 4) in the table above. I'm not sure why FM is interpreting 1Red and 1Blue as the same category, or perhaps I'm just misunderstanding what the GetSummary function does.
The Clients table looks like this:
Where:
The portal records are sorted on ClientIDCategory
Issue 2 that I'm stumped on:
I only want rows with a ShowInPortal value equal to 1 should appear in the portal. I tried creating a portal filter with the following formula: Table 1 Get Summary Method::ShowInPortal = 1. However, using that filter removes all row from the portal.
Any help is greatly appreciated.
One solution is to use ExecuteSQL to grab the Max Date. This removes the need for Summary functions and sorts, and works as expected. Propose to return it as number to avoid any issues with date formats.
GetAsTimestamp (
ExecuteSQL (
"SELECT DISTINCT COALESCE(MaxDate,'')
FROM Survey
WHERE ClientIDCategory = ? "
; "" ; "";ClientIDCategory )
)
Also, you need to change the ShowInPortal field to an unstored calc field with:
If ( GetAsNumber(Date) = MaxDateGroupSQL ; 1 ; 0 )
Then filter the portal on this field.
I can send you the sample file if you want.

SELECT previous year data based on year column

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.

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.