MDX Query returns value in report but not in Visual Basic code - vb.net

This is for an application which dynamically sets data for and renders reports.
I have an MDX query for a report which relies on a parameter. The query is:
SELECT NULL ON COLUMNS, strtomember(#DateYear) ON ROWS FROM [MYDATACUBE]
When running this in the report query designer, it returns a value properly. However, when running this in visual basic code, it returns nothing. Here is the important part of my code:
Dim cn = New AdomdConnection(adomdparamconnectionstrings(countparamsadomd))
Dim da As AdomdDataAdapter = New AdomdDataAdapter()
Dim cmd = New AdomdCommand(paramcommands(countparamsadomd), cn)
Dim tbl = New DataTable
If (adomdparams) Then 'If there are parameters, adds them to the query
For l As Integer = 0 To (countparamsadomd - 1)
If (adomdparamconnectionstrings(l) = "NODATASET") Then
Dim p As New AdomdParameter(paramvaradomd(l), paramadomd(l))
cmd.Parameters.Add(p)
Else
Dim p As New AdomdParameter(paramvaradomd(l), adomdqueryvalues(l))
cmd.Parameters.Add(p)
End If
Next
End If
da.SelectCommand = cmd
cn.Open()
da.Fill(tbl)
cn.Close()
I know the connection string works because all the other datasets use the same one. I know the command is right using break points. I know the parameter's value is right also using break points. I know the code overall works because it works with every dataset I've tested it with except this one. Using break points, everything seems to work as it does for other datasets, but then it just doesn't return any values.
The table resulting from this does have a properly named column ([Date].[Year].[Year].[MEMBER_CAPTION]) but has no rows. The returned value should be a single row with the year in it.

I have done something similar in C#.
If you have access to the dimension and hierarchy name, then you can have a generic query like this:
WITH
MEMBER [Measures].[Member Caption] AS StrToMember(#Hierarchy).HIERARCHY.CURRENTMEMBER.MEMBER_CAPTION
SELECT
[Measures].[Member Caption] ON COLUMNS,
StrToMember(#DateYear) ON ROWS
FROM
[MYDATACUBE]
Note that you'll have to add the parameter, #Hierarchy, to the command before executing the query. There is a little trick in the value expression for the calculated member. When passing a hierarchy expression to StrToMember(), it returns the default member from that hierarchy. From the default member, you can use the HIERARCHY function to work backwards to get the hierarchy. From the hierarchy, you can get the CURRENTMEMBER and its MEMBER_CAPTION property.
If you want a query specific for the hierarchy in your example, you can use:
WITH
MEMBER [Measures].[Member Caption] AS [Date].[Year].CURRENTMEMBER.MEMBER_CAPTION
SELECT
[Measures].[Member Caption] ON COLUMNS,
StrToMember(#DateYear) ON ROWS
FROM
[MYDATACUBE]

Related

Retrieving the Query used for a OleDBCommand

I'm currently using the following VB code to make a query against an Access Database, I would like to know is it possible to obtain what the SELECT statement that is being run and send that output to the console.
Dim QuestionConnectionQuery = New OleDb.OleDbCommand("SELECT Questions.QuestionID FROM Questions WHERE Questions.QuestionDifficulty=[X] AND ( Questions.LastDateRevealed Is Null OR Questions.LastDateRevealed < DateAdd('d',-2,Date() ) AND Questions.LastUsedKey NOT LIKE ""[Y]"" );", QuestionConnection)
QuestionConnectionQuery.Parameters.AddWithValue("X", questionDifficulty.ToString)
QuestionConnectionQuery.Parameters.AddWithValue("Y", strDatabaseKey)
Right now when I try to use: Console.WriteLine("Query: " & QuestionConnectionQuery.ToString)
I only get this:
Loop Question #1
Query: System.Data.OleDb.OleDbCommand
The short version comes down to this:
QuestionConnectionQuery.ToString
The QuestionConnectionQuery object is much more than just the text of your command. It's also the parameters, execution type, a timeout, and a number of other things. If you want the command text, ask for it:
QuestionConnectionQuery.CommandText
But that's only the first issue here.
Right now, your parameters are not defined correctly, so this query will never succeed. OleDb uses ? as the parameter placeholder. Then the order in which you add the parameters to the collection has to match the order in which the placeholder shows in the query. The code in your question just has X and Y directly for parameter placeholders. You want to do this:
Dim QuestionConnectionQuery AS New OleDb.OleDbCommand("SELECT Questions.QuestionID FROM Questions WHERE Questions.QuestionDifficulty= ? AND ( Questions.LastDateRevealed Is Null OR Questions.LastDateRevealed < DateAdd('d',-2, Date() ) AND Questions.LastUsedKey NOT LIKE ? );", QuestionConnection)
QuestionConnectionQuery.Parameters.Add("?", OleDbType.Integer).Value = questionDifficulty
QuestionConnectionQuery.Parameters.Add("?", OleDbType.VarChar, 20).Value = strDatabaseKey
I had to guess at the type and lengths of your parameters. Adjust that to match the actual types and lengths of the columns in your database.
Once you have made these fixes, this next thing to understand is that the completed query never exists. The whole point of parameterized queries is parameter data is never substituted directly into the sql command text, not even by the database engine. This keeps user data separated from the command and prevents any possibility of sql injection attacks.
While I'm here, you may also want to examine the WHERE conditions in your query. The WHERE clause currently looks like this:
WHERE A AND ( B OR C AND D )
Whenever you see an AND next to an OR like that, within the same parenthetical section, I have to stop and ask if that's what is really intended, or whether you should instead close the parentheses before the final AND condition:
WHERE A AND (B OR C) AND D
This will fetch the command text and swap in the parameter values. It isnt necessarily valid SQL, the NET Provider objects haven't escaped things yet, but you can see what the values are and what the order is for debugging:
Function GetFullCommandSQL(cmd As Data.Common.DbCommand) As String
Dim sql = cmd.CommandText
For Each p As Data.Common.DbParameter In cmd.Parameters
If sql.Contains(p.ParameterName) AndAlso p.Value IsNot Nothing Then
If p.Value.GetType Is GetType(String) Then
sql = sql.Replace(p.ParameterName,
String.Format("'{0}'", p.Value.ToString))
Else
sql = sql.Replace(p.ParameterName, p.Value.ToString)
End If
End If
Next
Return sql
End Function
Given the following SQL:
Dim sql = "INSERT INTO Demo (`Name`, StartDate, HP, Active) VALUES (#name, #start, #hp, #act)"
After parameters are supplied, you can get back this:
INSERT INTO Demo (`Name`, StartDate, HP, Active) VALUES ('johnny', 2/11/2010 12:00:00 AM, 6, True)
It would need to be modified to work with OleDB '?' type parameter placeholders. But it will work if the DbCommand object was created by an OleDBCOmmandBuilder, since it uses "#pN" internally.
To get or set the text of the command that will be run, use the CommandText property.
To print the results, you need to actually execute the query. Call its ExecuteReader method to get an OleDbDataReader. You can use that to iterate over the rows.
Dim reader = QuestionConnectionQuery.ExecuteReader()
While reader.Read
Console.WriteLine(reader.GetValue(0))
End While
reader.Close()
If you know the data type of the column(s) ahead of time, you can use the type-specific methods like GetInt32. If you have multiple columns, change the 0 in this example to the zero-based index of the column you want.

Defining an expression or code with lookup RS

I need to migrate a CR report to RS.
I have two Methods, that are basically two queries, one is called "Products" the other one is called "Volume".
Both these datasets have a field called ID.
The idea is to count ProductsID = VolumeID. Can I use a lookup as an expression inside RS that counts when lookup is not equal to Nothing? Or I have to do a code to do that?
EDIT:
Let me add more information.
Crystal Report is managing these in the Data Layer of the web app like this:
DataRow drVol = vols.Rows.Find(new Object[] { id, idEzd });
if (drVol != null)
{
if (drVol["Volume"] != System.DBNull.Value)
cRow.Volume = (isNormalReport ? Convert.ToDecimal(drVol["Volume"]) : Convert.ToDecimal(drVol["Volume"]));
else
cRow.Volume = 0;
dset.Compradores.Rows.Add(cRow);
}
So in this case I verified that RS is giving me an X amount of rows while CR is giving me another amount of rows.
The Idea is to duplicate this on an expression or VB code inside RS.
As you can see, we have to filter the drVol != null, RS is bringing all the rows.
I know a Lookup function can be used in this case as:
Lookup(Fields!id.Value & Fields!idEzd.Value, Fields!id.Value & Fields!idEzd.Value,Fields!Givemethisfield.Value,"Dataset")
But can I use this Lookup expression inside a code to count?
The counter should be something like this (Then I should add the lookup or an if that equals that vlookup, can the code see the two datasets?):
Dim count=0 as integer
Public Function Counter() as Integer
count = count + 1
return count
End Function
Public Function GetCounter () as integer
return count
End Function
Then create a Calculated Field for example "Counter" with the expression
=Code.Counter()
And then make the sum of that calculated field:
=Sum(Fields!Counter.Value)
But is not working, not even the counter, is giving me 0.
Sorry if I made a mistake in the Visual Basic code, I don't program in Visual.
EDIT2:
I saw some workaround on MSDN, something like:
Lookup(Fields!id.Value & Fields!idEzd.Value, Fields!id.Value & Fields!idEzd.Value,Fields!Givemethisfield.Value,"Dataset").Lenght
It makes sense as a lookup is an Array.
The problem is, either if I choose one scope or the other, I still have the problem that some fields can't be seen for example if the fields id and idezd are on Dataset1 and Givemethisfield is on Dataset2, it will either not see the id,idEzd or Givemethisfield.

VB.NET tabledapter query insert into from another dataset

I have a situation where I want to Insert into access DB table from MS SQL table.
Same columns and everything.
I have both data sets and both table adapter. I can do what ever I want inside each dataset - any manipulation but I cannot insert from one table to another.
I tried creating an Insert query for destination tableadapter but I cannot get the from working. Tried linking, nothing works.
Searched for days, simply cannot find it.
Thank you for your answer. Can you help me on my example. I'm having trouble setting this up. This is what i got:
Dim myToTableTableAdapter As FirstDataSetTableAdapters.ToTableTableAdapter
myToTableTableAdapter = New FirstDataSetTableAdapters.ToTableTableAdapter()
Dim myFromTableTableAdapter As SecondDataSetTableAdapters.FromTableTableAdapter
myFromTableTableAdapter = New SecondDataSetTableAdapters.FromTableTableAdapter()
myFromTableTableAdapter = myToTableTableAdapter.Clone
'but it doesnt work from here`
What I wanted to do is:
For each drfrom As DataRow In myFromTableTableAdapter.GetData
myToTableTableAdapter.InsertInto(drfrom.item(column01), drfrom.item(column02), drfrom.item(andSoOn))
Next
But it seem to me that this would take so much longer then a "Insert Into From Select" script.
You cannot insert a row from one table into another table, but there are a couple of ways to do what you want. One way (a little verbose) is this:
' sets it up with same schema but empty rows
mOutTable = inTable.Clone
' Now insert the rows:
For Each rowIn In inTable.Rows
r = mOutTable.NewRow()
For Each col In inTable.Columns
r(col.ColumnName) = rowIn(col.ColumnName)
Next
mOutTable.Rows.Add(r)
Next
mOutTable.AcceptChanges
A second way, which is more concise, is this:
outTable = inTable.Clone
For Each inRow As DataRow In inTable.Rows
outTable.LoadDataRow(inRow.ItemArray, False)
End If
outTable.AcceptChanges
Note that both inTable and outTable are ADO.NET DataTable objects. You cannot implement my suggestion on the DataAdapter objects. You must use the DataTable objects. Each DataTable can be associated with a DataAdapter in the standard fashion for ADO.NET:
Dim t as New DataTable()
a.Fill(t);
where a is the ADO.NET DataAdapter. I hope this helps!
Jim

Return Max value with LINQ Query in VB.NET?

I have a method that takes in an id, a start date, and and end date as parameters. It will return rows of data each corresponding to the day of the week that fall between the date range. The rows are all doubles. After returning it into a DataTable, I need to be able to use LINQ in VB.NET to return the maximum value. How can I achieve this? Here is the initial setup?
Dim dt as DataTable = GetMaximumValue(1,"10/23/2011","11/23"/2011")
'Do linq query here to return the maximum value
The other alternative is to just return a Maximum value just given an id which would be slightly easier to implement, so the method would look like this:
Dim dt as DataTable = GetMaximumValue(1)
'Do linq query here to return maximum value
Important
What if I want to query against a DataRow instead of a DataTable and the column names are not the same, they are something like MaxForMon, MaxForTue, MaxForWed`, etc... and I need to take the Maximum value (whether it is MaxForMon, MaxForTue, or some other column). Would an option for this be to alias the column returned in the stored proc? The other thing I thought about was instead of doing this in LINQ, I probably should just handle it in the T-SQL.
You can use the DataSet Extensions for Linq to query a datatable.
The following query should help.
Dim query = _
From value In dt.AsEnumerable() _
Select value(Of Double)("ColumnName").Max();
If you don't now in which column will hold the maximum you could use: (C#)
var result = from r in table.AsEnumerable()
select r.ItemArray.Max();

Perform an aggregation on a DataTable with Linq in vb.net

Given a datatable, I wish to output an IEnumerable type (dictionary(of T) would be perfect, otherwise a datatable would be acceptable) that provides an aggregation of the data within the datatable.
In SQL I would write the query as such:
select groupByColumn, sum(someNumber) from myTable group by groupByColumn
In VB.NET the closest I have got to this (achieved using information here) is:
' dt is a datatable containing two columns, referred to by index in p below
Dim q = From p In dt Group p By p(0) Into Sum(p(1)) Select p(0), SumOfNumber = Sum
However I receive error: "Range variable name can be inferred only from a simple or qualified name with no arguments." on the p(0) element.
Therefore my question is as follows:
How can I resolve this error?
How do I process the result (q) as an IEnumerable type?
Life isn't made any easier because I'm using Linq in unfamiliar vb.net. Many examples are in C#, but I haven't really come across anything suitable even in C#.
Resolved: I had to alias the columns returned.
Dim q = From p In dt
Group p By transactionTypeName = p(0) _
Into totalForType = Sum(Convert.ToDouble(p(1))) _
Select transactionTypeName, totalForType
Also note that I had to do a conversion on the value that I was summing because being a dynamically returned datatable didn't have the column type specified.
Hint found here because vb.net oh so helpfully gives us a different error message to C#'s "Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access."
For completeness, results are processed as below:
For Each item In q ' no need for defining item as a type (in C# we'd use the var keyword)
If item.totalForType = magicNumber Then
'do something
Else
'do something else
End If
Next