Context
I have a interface in VB.NET that extract the data from the UniVerse using UniObjects for .NET
Problem
From the COB file I need to get all keys where the FEC.COB field is equal to a specific date and the field SEC is equal to 04.
An expert in UniVerse Database told me that I can run the follow queries:
SELECT COB WITH FEC.COB > “31/10/2013”
SELECT.ID 1 2 04
But I don't know how can I do that with UniObjects library. Can anyone help me?
I don't use UniObjects as my shop normally gets data our of UniVerse via ODBC. Also my VB is bad, so I don't have much metacode for you, but the basic idea would be to do something like this.
1.) Create a UV Session. Hopefully you have that much worked out as I can be of next to no help there.
2.) Once the session is established Execute your query by doing something like this
session.Command.Text = "SELECT COB WITH FEC.COB > '31/10/2013'"
session.Command.Exec
(I converted your double quotes to single quotes and Universe won't mind).
3.) If you just need the IDs, you can get them by iterating through the select list that your query returns. A command line query will always return to list 0 unless you specify otherwise in your UV query. In most cases your results will be in session.SelectList(0)
Dim objSelect As object
Set objSelect = objSession.SelectList(0)
4.) It looks like the SelectList object has a ReadList method which returns a Dynamic Array Object, which you should be able to iterate through using normal array looping. Additionally you can use a while loop and next to do what you need to do.
Dim someObject as Object
someObject = objSelect.Next ' Get first ID
Do While Not objSelect.LastRecordRead
' Do something here with someObject. Maybe ToString it or something
someObject = objSelect.Next' Get next ID
Loop
Hope that is somewhat helpful.
Related
I'm hoping someone can help answer my question, perhaps with an idea of where to go or whether what I'm trying to do is not possible with the way I want to do it.
I've been asked to write a set of rules based on the data held by our ERP form components or variables.
Unfortunately, these components and variables cannot be accessed or used outside of the ERP, so I can't use SQL to query the values and then build some kind of SQL query.
They'd like the ability to put statements like these:
C(MyComponentName) = C(MyOtherComponentName)
V(MyVariableName) > 16
(C(MyComponentName) = "") AND V(MyVariableName) <> "")
((C(MyComponentName) = "") OR C(MyOtherComponentName) = "") AND V(MyVariableName) <> "")
This should be turned into some kind of query which gets the value of MyComponentName and MyOtherComponentName and (in this case) compares them for equality.
They don't necessarily want to just compare for equality, but to be able to determine whether a component / variable value is greaterthan or lessthan etc.
Basically it's a free-form statement that gets converted into something similar to an IF statement.
I've tried this:
Sub TestCondition()
Dim Condition as string = String.Format("{0} = {1}", _
Component("MyComponent").Value, Component("MyOtherComponent").Value)
If (Condition) Then
' Do Something
Else
' Do Something Else
End If
End Sub
Obviously, this does not work and I honestly didn't think it would be so simple.
Ignoring the fact that I'd have to parse the line, extract the required operators, the values from components or variables (denoted by a C or V) - how can I do this?
I've looked at Expression Trees but these were confusing, especially as I'd never heard of them, let alone used them. (Is it possible to create an expression tree for dynamic if statements? - This link provided some detail on expression trees in C#)
I know an easier way to solve this might be to simply populate the form with a multitude of drop-down lists, so users pick what they want from lists or fill in a text box for a specific search criteria.
This wouldn't be a simple matter as the ERP doesn't allow you to dynamically create controls on its forms. You have to drag each component manually and would be next to useless as we'd potentially want at least 1 rule for every form we have (100+).
I'm either looking for someone to say you cannot do this the way you want to do it (with a suitable reason or suggestion as to how I could do it) that I can take to my manager or some hints, perhaps a link or 2 pointing me in the right direction.
If (Condition) Then
This is not possible. There is no way to treat data stored in a string as code. While the above statement is valid, it won't and can't function the way you want it to. Instead, Condition will be evaluated as what it is: a string. (Anything that doesn't boil down to 0 is treated as True; see this question.)
What you are attempting borders on allowing the user to type code dynamically to get a result. I won't say this is impossible per se in VB.Net, but it is incredibly ambitious.
Instead, I would suggest clearly defining what your application can and can't do. Enumerate the operators your code will allow and build code to support each directly. For example:
Public Function TestCondition(value1 As Object, value2 As Object, op as string) As Boolean
Select Case op
Case "="
Return value1 = value2
Case "<"
Return value1 < value2
Case ">"
Return value1 > value2
Case Else
'Error handling
End Select
End Function
Obviously you would need to tailor the above to the types of variables you will be handling and your other specific needs, but this approach should give you a workable solution.
For my particular requirements, using the NCalc library has enabled me to do most of what I was looking to do. Easy to work with and the documentation is quite extensive - lots of examples too.
I'm working in Visual Basic and using OleDb to access an Excel spreadsheet. I'm importing the data from the sheet into my DataGridView, and that works fine, but now I'm working on filtering. For the most part it works great, but I'm trying to use parameters ("#p1" and so on), and I'm getting a very strange issue.
I can have the following (excluding a bunch of irrelevant stuff before, in between, and after)
query = query & "Project" & " LIKE #Gah1"
...
MyCommand.SelectCommand.Parameters.AddWithValue("#Gah1", "%House%")
and it gives me the results I'm looking for. But I can't seem to get a parameter for the name of the column itself, for example
query = query & "#Gah1" & " LIKE #Gah2"
...
MyCommand.SelectCommand.Parameters.AddWithValue("#Gah1", "Project")
MyCommand.SelectCommand.Parameters.AddWithValue("#Gah2", "%House%")
does not work (and I've tried enclosing Project in different brackets and stuff in different ways, can't get it to work). I've found plenty of examples on using parameters, but none that use them to give the column name.
I'm guessing the parameter changes how the string is represented, seeing as you don't need to have the ' ' around string literals.
Is it not possible to give column names in parameter? If you can, what do I need to do?
Well it won't let me post comment, so here
a) Oops, no, I guess not
b) The string query that I end up sending in my test query here is
"select * from [Bid Summary$] where #Gah1 LIKE #Gah2"
I can post the procedure if absolutely need be, but it isn't the problem because the whole thing works perfectly fine if I replace #Gah1 with Project or [Project], so I just showed the lines that I change.
I'm very new to parameterized queries, can you explain how to avoid query strings using it? If there's a better way to do what I'm doing I'm happy to use it =)
And thanks for response and edit
I use combination of string methods and parameters, like this:
//replace field name in a query template
query = String.Format("select * from [Bid Summary$] where {0} LIKE ?", "#Gah1");
//set value (name is in OleDb parameter ignored, so it could be null)
MyCommand.SelectCommand.Parameters.AddWithValue(null, "%House%");
Note: There is possibility of a sql injection, so be sure about origin of field name (not from user input).
I'm just looking to be able to sort the results of a BatchedJoinBlock (http://msdn.microsoft.com/en-us/library/hh194683.aspx) so that the different results of the different targets stay together. I will explain! Example in some pseudo-code:
Dim batchedJoin = New BatchedJoinBlock(Of String, object)(4)
batchedJoin.Target1.Post("String1Target1")
batchedJoin.Target2.Post(CType(BuildIt, StringBuilder1))
batchedJoin.Target1.Post("String1Target2")
batchedJoin.Target2.Post(CType(BuildIt, StringBuilder2))
Dim results = batchedJoin.Receive()
'This sorts one result...
Dim SortByResult = results.Item1.OrderBy(Function(item) item.ToString, New NaturalStringComparer)
Basically I've got a string and an object, the SortByResult variable above sorts the strings exactly as I'd like them to sort. I'm looking for a way to get the objects that used to be at the same index number in target2 into the same order. e.g. if "String1Target1" changes order I'd like to somehow reliably refer to/pair it together with "StringBuilder1". The actual end result just needs to be that the objects (target2) are sorted in the order that is dictated by the strings being sorted (target1). Something like:
Dim EndResult = results.Item2.OrderBy(strings in target1)
but I'll gladly take an intermediate solution! I've also tried using a dictionary (results.Item2.ToDictionary) with the string as a key (which would also be a fine solution) but it's a bit beyond my ken using lamba expressions in the proper context. I can realistically do this in several steps with a list or something, but I'm trying to get something more efficient/learn something, and it seems like there's a lot of default options with the results of the jointblock that I'm just not experienced enough to use. Thanks in advance for any help you can provide!
To me, it looks like you don't actually want BatchedJoinBlock, because the two pieces of data always come together. A better option for that would be a BatchBlock of Tuple<string, object>. When you have that, you can then use LINQ directly to sort each batch:
results.OrderBy(Function(tuple) tuple.Item1)
I declared my Array:
Dim invoice_discountitems(100, 100) As String
Set Values into array:
For i As Int16 = 0 To data_set.Tables("discount_items").Rows.Count - 1
invoice_discountitems(i, 1) = data_set.Tables("discount_items").Rows(0).Item("item_code")
invoice_discountitems(i, 2) = data_set.Tables("discount_items").Rows(0).Item("discountitem_average")
Next
Now I try to find a single value:
Dim res As String
res = Array.IndexOf(invoice_discountitems, "FO1506")
MsgBox(res)
But, I get this error :(
"Only single dimension arrays are supported here"
This is a fundamentally wrong approach - for a number of reasons
You're treating ALL the data points as Strings
You're not taking advantage of DB optimisations like indices
You're loading data into memory that you're never going to use (at least int he example)
The Nicest way to do it would be with Linq-To-Entities:
Dim Record = MyDBContext.Discount_Items.Where(function(x) x.ItemCode = "FO1506").Single
Console.WriteLine(Record.discountitem_average);
If you're stuck with your current Data Access Layer, you need to modify the SQL being executed to only return the information you're interested in. Without more information, I can't provide decent example code but you want the SQL to end up looking like this...
SELECT itemcode,
discountitem_average,
[Other fields],
FROM MyDatabase.M
EDIT: To Clarify, there are a number of ways to access data in a database. The one I prefer is LINQ-To-Entities (Have a look through this tutorial).
In short, you add a new Item to your project and point it at your database. This becomes your "Database Context" - it represents the database and that's how you run queries.
Project -> Add -> New Item...
Select ADO.Net Entity Data Model (Linq-To-Entities is almost Identical to Linq-To-Sql but more recent and better supported - use Entities until you know the difference)
Call it something like MyDBContext
When prompted, choose "Generate From Database" and point it at your database.
It's worth noting that the designer takes advantage of information in the database like Foreign Key Constraints - So the better your database is designed, the better the model it will create.
Then, you refer to it in code as shown in my first example.
First of all IndexOf return int as index!
To get the index of string
Try:
Dim i As int
Dim j As int
i = Array.IndexOf(invoice_discountitems.OfType(Of String)().ToArray(), "FO1506")
j = i MOD 100
i= i/100
MsgBox(i+" "+j)
(I use c# but I think it's not different)
I want to update a custom user field in QC using the Label of field instead of the name
At the moment we are doing it this way
Set currentRun = QCUtil.CurrentRun
currentRun.Field("RN_USER_03") = 1
currentRun.Post
But I would like to do it this way
Set currentRun = QCUtil.CurrentRun
currentRun.Field("Data Rows Passed") = 4
currentRun.Post
But I can't find the method to do it with.
Any Ideas?
Implying all labels are unique (which I doubt..):
You could create a function which accepts a label, searches in QC's tables that define customized fields for the correct field definition, and returns the field name. Then use the function's result value as the indexed property's index.
Suppose that function would be called "GetNameOfLabel", then the Caller's code would look like:
Set currentRun = QCUtil.CurrentRun
currentRun.Field(GetNameOfLabel ("Data Rows Passed")) = 1
currentRun.Post
Of course, the function would not really be trivial, but easy enough after some digging in the QC data model and finding an efficient way to fetch the name from the DB via SQL.
Or, the function could look up the name in an array, or a dictionary, then you would have to maintain that dictionary, but you would not have to go to the database for each lookup.
Disadventages:
Scripts with the wrong label might be harder to be debug
If labels are not unique, it might be real "fun" to debug
If looking up on the DB:
All scripts slow down if you don't cache, or pre-load, SQL query results for those lookups;
complexity, as you have to do the right SQL query, and you depend on QC's data model in a quite peculiar way (usually a horror when you're upgrading)
If looking up in an array, or dictionary:
You either must maintain its initialization (bet other admin guys adding a cust field will forget that easily), or must "load" it from QC's table (which is a bit like the SQL solution above, and has the same downsides).
I'd go with the array/dictionary-initialized-from-db-idea. Or, if you can live with the constant idea already presented, that one is a good bet. Considering that there is no session-independent scope in QCs customizing scripts, the SQL access idea might really kill performance because it would have to be executed for every new user session. Which is why I, too, +1'd the constant idea.
Look at this:
Dim gFieldLabelToNameDICT: Set gFieldLabelToNameDICT = CreateObject("Scripting.Dictionary")
gFieldLabelToNameDICT.CompareMode = vbTextCompare
Function GetNameOfLabel (strFieldLabel)
' If it doesn't exist yet in fieldLabelToName dict -> search it using TDC and add it to the list to improve performance
If Not gFieldLabelToNameDICT.Exists(strFieldLabel) Then
Dim testSetFields As List
Dim testSetFields: Set testSetFields = QCUtil.QCConnection.Customization.Fields.Fields("RUN")
For Each aField in testSetFields
If aField.UserLabel = strFieldLabel Then
gFieldLabelToNameDICT.Item(strFieldLabel) = aField.ColumnName
End If
Next aField
End If
GetNameOfLabel = gFieldLabelToNameDICT.Item(strFieldLabel)
End Function
Maybe you shall want to add some more error handling, such us considering the case that the label is not found.