Hey all,
My problem:
I'm trying to create a scope function that calculates exchange rates based on a date and currency AND a specified rate.
I have this working fine, but within my scope function I want to say - if Dimension.Attribute.member = "Latest" then use FXRate 1 otherwise use FXRate 2.
Now I even have that "working"... BUT that only works if the member is in my dataset... i.e. I can't use it as a parameter\filter.
I don't want to have to tell the users "you always have to have LatestFlag in every report... just hide the column"
I want to give the user the ability to set the report parameters before he starts analysing the data.
So here's a snippet of my code so far:
Scope ( { Measures.[Amount]} );
Scope( Leaves([ExchangeDate]), [Reporting Currency].[USD],Leaves([Currency]));
Scope( { Measures.[Amount]});
This = iif(
[Latest Flag].[Flag].CURRENTMEMBER.name = "Yes",
[Reporting Currency].[Local] / Measures.[Rate2],
[Reporting Currency].[Local] / Measures.[Rate]
);
End Scope;
End Scope;
End Scope;
I suspect I need to use another Scope instead of the iif - but I'm not sure how to implement.
Any ideas?
Oh it's probably important to note.
The FXRate table has two rates.
Rate is updated daily.
Rate2 is repeated for every currency everyday.
So irrevelant of the date, Rate2 will always be the latest rate for that currency.
The LatestFlag dimension is merely a table with yes and no and doens't have any relationship to any other table.
I'm just using it as a filter.
There is a flag on the FX table too - but I'm not using this as I need the date to be considered if it's not the latest rate.
My solution was solved in the following link.
http://social.msdn.microsoft.com/Forums/en/sqlanalysisservices/thread/cb5ec22b-65c0-4b86-9879-40e42c039808
Related
I actually have two questions.
I'm a beginner user of Access, still trying to get a good understanding of the software. I'm trying to create a database for a library (School project) with a borrowing out system. I have two fields in a table called DueDate and DateHired. The DueDate functions on the expression =Now()+28 and the DateHired function on the expression =Now(). Basically making the due date 4 weeks ahead of when the book was hired. My first question is quite simple; if I were to input a record today, would the two DueDate/DateHired fields remain the same date and time by tomorrow? Or would they update to the Now() of tomorrow?
My second question is something regarding an expression. I'm trying to make an Overdue checkbox. The premise is that if Now()>DateDue then the Checkbox will be 'Yes'. My current code in the ControlSource part is this:
=CBool([DateDue]
However, the checkbox simply displays '#Error' and not Yes/No. I'm also concerned that if the answer to the first question was '=Now() stays the same after the record is added and doesn't update' that would also mean the Overdue function would not really work unless you were inputting the record after the due date. Which would make no sense. Any suggestions?
Cheers.
This is relation to your second question. You can ask a separate question for the first part.
=CBool([DateDue]
What you are trying to do here, is convert a Date data type to a Boolean (you're missing the closing parentheses by the way) which is impossible.
What you should be doing is check if the due date is less than today and return the appropriate True/False value.
IIf([DueDate] < Date(), True, False)
Which means:
IIf("Is due date in the past?", "Yes, it's overdue", "No, it's not overdue")
You can read more about the IIf function here.
Indeed as a beginner, make it a habit to use the date functions. Later you can turn to "smarter" methods which, by the way, often aren't that smart.
1.
If you store a date, it remains unchanged in the table. And don't use Now unless you specifically need the hours too:
DateDue = DateAdd("d", 28, DateHired)
or in a query - using the designer:
DateDue: DateAdd("d",28,[DateHired])
or as a ControlSource for a textbox on your form:
=DateAdd("d",28,[DateHired])
2.
You can use DateDiff for this:
Due = DateDiff("d", DateHired, Date) > 28
or in a query - using the designer:
Due: DateDiff("d",[DateHired],Date()) > 28
or as a ControlSource for a textbox on your form:
=DateDiff("d",[DateHired],Date()) > 28
and set the Format property of the textbox to, say, Yes/No or something else meaningful for your users.
Trying to recreate this formula in Pentaho Mondrian Cube.
iif(ISLEAF([Time].[Month].CurrentMember)),[Measures].m1,0)
This formula is being used in SSAS cube already. Need to recreate similar formula in Pentaho Mondrian Cube.
Can IsLeaf be used in mondrian or is there any alternative for this?
Something like the following should work at any arbitrary level within the default hierarchy.
It will return [Mesures].m1 for the last year, which (has non-empty m1 measure and satisfies your filter conditions) in case you select [Time].[Year] members.
Or it will return the measure for the last month of the last year, which has non-empty m1 measure in case you select [Time].[Month] members.
Though I don't think it will work if you mix members for different levels (e.g. WITH SET time_members AS {[Time].[2017], [Time].[2017].[1]. [Time].[2017].[1].[31]})
If you don't need such a generic approach, then this solution may be simplified and the measure calculation speed may be improved.
Warning: the query may cause 100% CPU utilization (or may not, I am not sure), thus making your server unresponsive. So, choose your testing environment carefully.
Disclaimer: I don't have mondrian to test at the moment, so the following example is very likely to have errors.
Iif(
// Check if current [Time] member is the last member:
[Time].CurrentMember
IS
// Take the 0th member from the end of the set (AFAIK, mondrian sets are guaranteed to be ordered):
Tail(
// AFAIK, Level.Members returns the full set of members, disregarding query filters.
// So I use Filter function to filter members, which don't exist in context of current cell.
// It should leave only members, which (are related to current cell and satisfy filter conditions of the query).
Filter(
[Time].CurrentMember.Level.members
// If your measure is nullable, then you might want to use count measure in this condition instead of m1:
, NOT IsEmpty([Measures].m1)
)
// Number of members to get by the Tail() function:
, 1
// Return the only member of the set as a Member (not as a Set):
).Item(0)
// return if true
, [Measures].m1
// else:
, 0
)
Some points that may be problematic and need to be tested:
How the measure is calculated if the last [Time] member has empty m1
measure (if this is a valid case for your measure)
How the measure is calculated at different levels of [Time]
hierarchy.
How the measure is calculated if you don't use [Time] dimension in
report explicitly.
How the measure is calculated if you use [Time] dimension on slicer
axis only (in WHERE condition)
How the measure is calculated if you use restricted set of [Time]
members, e.g. explicitly enumerating members in set literal (e.g.
{[Time].[2006].[01], [Time].[2006].[02]}) or via using Filter()
function on the dimension.
How the measure is calculated if you use fiters on other
dimensions/measures.
How the measure is calculated at calculated members of [Time]
dimension (including totals and sub-totals generated by Analyzer).
How the measure is calculated if you select members from different
levels on the same axis.
I am developing a db (MS access 2010) to support a school with a well-defined model for tuition quotation. The list of products is assembled for each quote, then various discounts are applied. The discounts may be a percentage or an absolute dollar amount. So far, so easy. The problem is that their business logic requires:
No limit on number of discounts.
Specific discounts to be applied in a defined sequence (implemented in my case with a "discount ordinal" column, values 1 (first applied) to 100 (last applied).
Each sequential application of a discount is to the running total of the quote. Eg: Total products $1000. Discount: 50%. Value: $500. Subtotal $500.
Subtotal: $500. Discount: $25. Value: $25. Subtotal: $475.
Subtotal: $475. Discount: $10%. Value: $47.50. Subtotal: $427.50.
This appears to be a variation of the "get the value of the field in the previous row" problem, but with the added twist that the "value of the field" is actually a cumulative calculation. It has the flavor of recursion: while discounts remain, subtotal(previous subtotal).
I have no clear idea how to implement this in a report, because the calculation as noted above is self-referential. I'm not looking for code here, just guidance on the general approach to the problem (ie, some kind of global variable, using VBA - in which case, I'm not sure what the "glue" between the query in VBA and the report would be - or some trick with a calculated field although I've spent a lot of time trying to figure one out). Any thoughts?
In that kind of situations, I always create a new table, that will get filled up when the report opens, and base the report in that table, not the original one. That way I can do all the calculations I need, even making several passes. The report then is simply a "dump" of the table. Complex totals can be additional columns, that will be shown only in the totals section.
You could have table for purchase history using an integer to link each purchase since an autonumber by itself will not link each discount stage.
So in excel I would use something like this:
v = Starting Value
i = 1
Do Until i = Last Discount
d = ws.Cells(i, 9).Value
v = v * (1 - d)
ws.Range("B2").Value = v
i = i + 1
Loop
At each stage you could write to the table (using docmd.runsql) the discount applied (d) and the value (v) but it could be quite slow. You could then order the table by purchase identifier then descending by value since sequential discounts will inherently order correctly.
I have an SSRS report with information about invoices my company has issued with things like amount, date, payment, amount due, etc. I'm using SSRS 2008 (not R2) in Visual Studio 2008 environment. My issue has to do with formatting the report using the Expression editor. Currently, an invoice will be formatted as silver if the invoice has an Amount Due (a column) over 0.01 (outstanding invoice). We also issue credits (negative amounts) and these are almost always the negative amount of a previous invoice.
So, an invoice that has a credit issued to it will still show as silver because it's amount due > 0.01. But if there is a credit issued to this invoice, it's not actually outstanding and should be white. For example, if an invoice is $100.00, and there is a credit after for ($100.00), the original invoice's background color should be switched to white.
Here's where the code explanation comes in. I thought this was possible with custom VB code in the report, but it seems like the Expression editor in SSRS will not recognize my function as it says "Unrecognized identifier". I googled this a bit, and most of the topics I came across said that it will show that but actually work anyway. Well, I'm pretty sure it's not working at all, because I put this in as my expression and got all white cells for a certain column:
=IIF(Fields!Amount_Due.Value > 0.01,
IIF(Code.HasCredit(Fields!Amount_Due.Value) = True, "Blue", "Silver"), "Red")
The HasCredit function is below.
Function HasCredit(ByVal currentAmt as Double) As Boolean
Dim i as Integer
Dim amt as Double
Dim amts as System.Collections.ArrayList = New System.Collections.ArrayList()
Dim negativeAmt as Double
Dim retValue as Boolean = "False"
i = 0
For i = 1 to Report.Parameters!Test.Count()
amt = Report.Parameters!Test.Value(i)
amts.Add(amt)
Next
negativeAmt = currentAmt * -1
If amts.Contains(negativeAmt) Then
retValue = "True"
End If
Return retValue
End Function
When these two pieces are run, I get white background for all cells of this column. I read something on the net saying that only Shared functions would work, but I found multiple other examples showing functions that were not shared that worked. When I make it shared, it gives me: BC30369 Cannot refer to an instance member of a class from within a shared method or shared member initializer without an explicit instance of the class. due to the Report.Parameters!Test.Count() line. I got the idea for using Report.Parameters from http://social.msdn.microsoft.com/Forums/en-US/sqlreportingservices/thread/a7d59224-0ee5-491e-883b-2e5fcb3edeab.
So to reiterate, what I'm basically trying to do is get the values of each cell in this column into a collection and, for any two amounts where one amount has a negative equivalent, give it a white background instead of silver.
In SSRS 2008 R2, even if you reference a valid custom code method, the expression editor may still warn that the identifier is invalid. This does not always mean that it's invalid. My function worked even though that warning was thrown.
After a bit more researching I figured out how to solve this. I basically had to add a textbox with the =Join() function so that all the values in a column were put in this textbox; then reference the textbox in custom code and use a boolean value in the expression editor. Detailed instructions below.
1) Add a multi-valued parameter to your report (Right click Parameters, Add Parameter). Name it, select Allow Multiple Values, select Hidden for parameter visibility. For Available Values tab, select Get values from a query. Point to your dataset, and set the Value field to the column you want the parameter to check. For me that was my Amount Due column. Label field is irrelevant and can be left blank. In the Default Values tab, do the same, make sure the value field is set to the same column as before. Under Advanced, select Never refresh.
2) Create a new textbox on your report. Might want to name it, ex txtColumnValues. Edit the expression and put this in: =Join(Parameters!YourParameter.Value, ",") This will get all of the field values from the column you specified in your parameter, each separated by a comma.
3) Edit your report's custom code and make a VB function (as Boolean) to check the textbox. For example here is my code.
Public Function HasCredit(Amt as Double, ri as ReportItems) as Boolean
Dim retValue as Boolean = False
If Amt > 0.00 AndAlso ri!txtAmounts.Value.Contains(Amt*-1) Then
retValue = True
End If
Return retValue
End Function
4) Finally go to your expression editor for the field you want to change. In my example I wanted to change the background color if the current field had a negative equivalent in one of the other fields (my textbox), so my code looked like this:
=IIF(Fields!Balance.Value > 0.01
AND Code.HasCredit(Fields!Balance.Value, ReportItems) = False, "Silver", "White")
I had to take separate pieces of info from 3 or 4 pages, put them together, and hope they worked... after about a week they did... I guess it's all about persistence. If you need any further help with this just let me know.
I have googled it a lot and found that usually it cannot be done. I came across one of the hacks here:
http://www.bp-msbi.com/2011/04/ssrs-cascading-parameters-refresh-solved/
But its not working for me in ssrs 2005. Just wondering if anyone else tried it in 2005.
Or is there any other hacks that can be tried.
As per this article the dependent parameter gets refreshed only when its values are invalidated by the selection in the first parameter. If we can invalidate the dependent parameter every time a parameter changes we will enforce a complete refresh. An easy way to do this is to attach a value such as a GUID obtained with the NEWID() T-SQL function.
So basically we want to introduce a fake parameter in between two real parameters. This fake parameter is supposed to return new values everytime as the storedproc behind it will add a guid to the resultset everytime that proc is called. So it forces complete refresh of the other parameters.
Now the main issue I am facing is :
Setting the default value of this fake parameter.
For the available values the storedproc behind the fake param runs and it returns data in the format say : result1,result2_GUIDFROMSQL
Now it looks like the same storedproc is called again to set the defult value if i ask it to get the default value from query. But as the storedproc is run again new guid comes and so the old value cannot be found so its not being set as desired.
I just need to figure out a mechanism to pass this guid from introduced param to the next param.
Thats where I am failing.
My issue can simply be replicated by creating a param whose datasource is this querystring.
select getdate() id, #name nid
So in this case how to set a default value for this param.
Below I'm going to present a detailed scenario and then I'll show the example implementation based on Visser and Abbi's answers.
Imagine that I have a report where each row is a project. A project has a Status column with values "In Progress" or "Complete" and a Project Manager column whose values are a person's name. Here are the rows in the table:
Project A (Status = In Progress, Project Manager = Bob)
Project B (Status = In Progress, Project Manager = Tom)
Project C (Status = Complete, Project Manager = Jack)
Project D (Status = Complete, Project Manager = Tom)
Project E (Status = Complete, Project Manager = Jill)
I want to have 2 parameters on my report
Show Completed Projects? - This is a boolean parameter
When false will only show "In Progress" projects A & B
When true will show "In Progress" projects A & B in addition to "Complete" projects C, D, & E.
Project Manager - This is a multi-value text parameter whose options and default values will need to change based on the Show Completed Projects? parameter upon which it is dependent.
If Show Completed Projects? is set to false then only "Bob" and "Tom" options will show up because they are the project managers for the in progress projects Project A & B respectively.
If Show Completed Projects? is set to true then in addition to "Bob" and "Tom" you will also have "Jack" and "Jill" show up as options because they are project managers for the inactive projects Project C & Project E respectively.
Now for the implementation:
Show Completed Projects? parameter
Project Managers dataset query (See Visser and Abbi's answers for details on how this generates a key that will change based on the independent parameter and will force SSRS to reload the default values)
SELECT
[ProjectManager_Key] =
pOuterAlias.[ProjectManager_Key] + '_' +
CAST(ROW_NUMBER() OVER(ORDER BY pOuterAlias.[ProjectManager_Key] DESC) AS NVARCHAR(MAX)),
[ProjectManager] = pOuterAlias.[ProjectManager]
FROM
(
SELECT
[ProjectManager_Key] =
pInnerAlias.ProjectManager + '_' +
CAST(ROW_NUMBER() OVER(ORDER BY pInnerAlias.ProjectManager ASC) AS NVARCHAR(MAX)),
[ProjectManager] = pInnerAlias.ProjectManager
FROM
(
SELECT
[ProjectManager]
FROM
[dbo].[Project]
WHERE
Status = 'In Progress' OR
#ShowCompletedProjects = 1
) pInnerAlias
) pOuterAlias
ORDER BY
pOuterAlias.[ProjectManager]
Project Manager parameter
General
Available Values
Default Values
Projects dataset
Query
SELECT
*
FROM
[dbo].[Project]
WHERE
(
Status = 'In Progress' OR
#ShowCompletedProjects = 1
) AND
Project Manager IN (#ProjectManager)
Parameters (Make sure to note the [#ProjectManager.Label] portion which will make it match the project on the actual project manager value from the database and not the key that we generated.
Finally I was able to resolve this. This link was a helpful start.
http://www.optimusbi.com/2012/07/16/multilevel-cascading-select/
Basically what it does is : Writing parameter query in a manner so that the dependent parameter changes its value every time you change its parent parameter.
The query adds rownumber with a "_" preceeding it. So every time user selects other values the rownumber changes and hence the query resultset.
Then when using the resultset remove the text after the underscore to get the real codes.
There is a workaround to fix this issue for all situations.
Note that the previous answer provided, writing parameter the query in a manner so that the dependent parameter changes its value every time you change its parent parameter, works for some cases but not all cases. If the dependent parameter's "Available" values change as a result of another parameter AND the dependent parameter is visible, it works, If the dependent parameter is hidden or internal, or if the "Available" values do not change as a result of the other parameter, it will not work.
The foolproof workaround is to define a custom function in the report and call it in an expression for the dependent parameter. The custom function takes the parameter's (the parameter that the dependent parameter depends upon) value as an argument, and simply returns its value. Even though function is simply taking the value and returning it, SSRS does not inspect the code and assumes that the code could do anything (generate a random number, pull files from disk, etc ...). So SSRS calls the function every single time the value changes regardless of whether the dependent parameter's "Available" values change, and regardless of whether the dependent parameter is visible, hidden, or internal.
Something like this:
public function returnArg(ByVal TheArg As String) As String
return TheArg
end function
Assume you have two parameters:
Parameter1
Parameter2
And that Parameter2 depends upon Parameter1.
Set Parameter2's value as an expression that includes the call to Parameter1 with the function, like:
=CODE.returnArg(Parameters!Parameter1.Value)
Now in this case Parameter2 simply displays the value in Parameter1, but this logic can be extended to more complex expressions and more than one parameter. As long as there is a CODE.returnArg(...) function call for each parameter in the expression, SSRS will always refresh the value.
#Abbi's second link is no longer valid. The article can now be found at:
http://www.optimusinfo.com/multilevel-cascading-select/
Unfortunately the images are broken and the article is a little incomplete without them. There is one image I was able to locate in its moved location (I could not find the others even with trying appropriate variations): http://www.optimusinfo.com/wp-content/uploads/2012/07/Multilevel-Cascading-with-Select-All-18.jpeg
This gave me the last clue I needed to get the technique working: when you're setting up the dataset parameter properties, you need to manually specify the parameter value as e.g. [#City.Label] rather than the usual plain old [#City] (which corresponds to [#City.Value].
This is because the technique alters the "Value" to a custom value which can no longer be looked up in the DB! So you need to use the user-friendly, db-existing "Label" as the parameter. (Probably should be obvious but...) The rest is all pretty standard if you understand report builder.
Also note that this link as it stands doesn't work with multi value cascading parameters. However it can easily be modified to do so: just alter the parameter stored procs to join to a value splitting function. I adapted mine from the udf_splitvarible function given at SQL Server - In clause with a declared variable
The modification is fairly simple but I'll give an example. This is from the article (with terrible formatting fixed).
SELECT
l11.CompanyName1+'_'+ CAST(row_number() over( order by l11.CompanyName1 desc) AS VARCHAR(50) )as CompanyName11
,l11.CompanyName
FROM
(
SELECT
l1.CompanyName+'_'+ CAST(row_number() over( order by l1.CompanyName asc) AS VARCHAR(50) )as CompanyName1
,l1.CompanyName
FROM
(
SELECT DISTINCT CompanyName
FROM Customers
WHERE City IN(#City)
)l1
)l11
ORDER BY l11.CompanyName ASC
Change:
SELECT DISTINCT CompanyName
FROM Customers
WHERE City IN(#City)
to:
SELECT DISTINCT CompanyName
FROM Customers
INNER JOIN udf_SplitVariable(#City,',') v ON City = v.Value
This all applies to SQL Server 2008/Report Builder 3 also.
I wasn't able to understand/apply #borkmark's answer. The limitations don't seem to apply to my case.