VB.NET -> Combobox random index - mysql

I have a question regarding comboboxes in VB.net 2010.
In my database I have 4 fields: e.g.:
idDetails | DetailsShortCode | Details_Explain | DetailsSortOrder
{autonum}1| DOA | Death on Arrival| 5
{autonum}2| NDI | No Display | 10
{autonum}3| QQA | In Research | 4
etc..
These values I pull out of the dbase (mySQL) and insert into a CheckedListBox.
I display the values as DetailsShortCode & " - " & Details_explain.
I use a [for loop] to create index numbers, because the sort order is based on the Details Sort order. Which means that VB.net gets 'fed' with the results in the following order:
idDetails | DetailsShortCode | Details_Explain | DetailsSortOrder
3 | QQA | " ... " | 4
1 | DOA | "...." | 5
2 | NDI | " ... " | 10
If i put this in the listbox, the error I shall receive is '3 is an incorrect value for 'index'"
Due to the fact that VB.net expects that the CheckedListBox (and also ComboBox) index always is in a sequential order, as in 0,1,2,3,4..etc..
The problem is the fact that orders in the database can change, items can change, and I have a field in another table containing a comma separated list of the details selected (e.g. 1;10;14;12;)
This means that 1 always must be the item with PrimaryKey 1, and that the displayed item on that index must always be the same...
so what I need, is to know how I can use the Primary Key as an Index Number, and let VB.Net not throw an error when the Index is in a random order.., or give the items a hidden value (like in HTML and PHP), in which I can just use the [for loop] indexes..
This is the code I use to insert items to the Details CheckedListBox
Function LoadComboBoxes(ByVal CB As String)
Dim SQLtext = ""
Select Case CB
Case "Details"
SQLtext = "Select " & _
"idDetails, " & _
"DetailsCode, " & _
"DetailsExplain, " & _
"DetailsSortOrder " & _
"FROM Details order by DetailsSortOrder"
Dim i = -1
Dim dr As MySqlDataReader
Dim cmd As New MySqlCommand(SQLtext, dbconn)
connect()
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection)
CLBDetails.Items.Clear()
While dr.Read
i += 1
CLBDetails.Items.Insert(i, .GetString(1) & " - " & dr.GetString(2))
End While
end select
end function

The easiest way might be to use a DataGridView which should have all the functionality of the CheckedComboBox plus the ability to keep the data intact as column data and allow you to reference the ID column as an ID Column rather than an ID aliased as an Index.
Depending on what a CheckedComboBox is (there are lots of these around) and what it inherits from, you should be able to store objects there:
Class DisplayItem
Friend ID as Long
DetailsShortCode As String
Details_Explain As String
DetailsSortOrder As String
Public Function overrides ToString As String
' depending on the CheckedComboBox add column seperators?
return DetailsShortCode & Details_Explain & DetailsSortOrder
End Function
End Class
Adding an object such as this to the control lets you retain the PK ref:
ccb.Items(N).ID
While the ToString function lets you format the output as desired. Further, you could create a List(Of DisplayItem) or BindingList(Of DisplayItem) and drive the CheckedComboBox by binding the datasource to the List. A DGV is still easier...

Related

ACCESS sql code to multiply record with calculated value from previous cell

I am using Access Database to get a value. I am fairly new to access as I usually use SQLServer and I am having trouble in getting what I want.
I have the following table, with column TARGET and incremental Target as the target column that I need to get:
Category|Period|Value| TARGET |
A | 4 | 1 | 1/1 =1 |
A | 3 | 3 | 1/(3*1)=0.33 | (1/value at period 3 * previous target)
A | 2 | 6 |1/(0.33*6)=0.505|
A | 1 | 9 |1/(0.505*9)=0.22|
The data is partitioned by Category and ordered in descending order by Period.
For the first row the Target should be: (1/value at current period)
For the next rows the Target should be: (1/value at current period * value of previous target)
As you can see this is somehow complex as I need to evaluate a cell value and then for the next row I need to use the value in the cell above.
Plus I need to get the incremental value for this column as well.
Any help will be very much appreciated as I am new to Access and need to get this done soon!
Here is a function placed in general module that can be called from query. Value is a reserved word so I used Data for that field name.
Option Compare Database
Option Explicit
Global dblTar As Double
Global strCat As String
____________
Function CalcTarget(strC As String, dblT As Double) As Double
If strCat <> strC Then
strCat = strC
dblTar = dblT
End If
dblTar = 1 / (dblT * dblTar)
CalcTarget = dblTar
End Function
Calling function in query:
SELECT Category, Period, Data, CalcTarget([Category],[Data]) AS Target FROM Table1;
Normally I advise not to save calculated data to table when a query can work, but if you prefer to save, then options are:
An UPDATE action: UPDATE Table1 SET Target = CalcTarget([Category],[Data]);
Or VBA:
Sub CalcTarget()
Dim rs As DAO.Recordset
Dim strCat As String
Dim dblTar As Double
Set rs = CurrentDb.OpenRecordset("SELECT * FROM table1 ORDER BY Category, Period DESC")
strCat = rs!Category
dblTar = rs!Data
Do While Not rs.EOF
If rs!Category = strCat Then
dblTar = 1 / (rs!Data * dblTar)
rs.Edit
rs!Target = dblTar
rs.Update
rs.MoveNext
Else
strCat = rs!Category
dblTar = rs!Data
End If
Loop
End Sub

Access 2013 - sequential numbering without autonumber

New to this site and access.
I am trying to create a database to keep track of a master log. Need help with sequential numbering. I currently have the following tables.
PO Table
POID - Autonumber(PK)
PODate - Date/Time
Supplier - String
Item Table
ItemID - Autonumber(PK)
POID - Ingeter(FK)
ItemDescription - String
Quantity - Integer
MasterLog Table
MasterLogID - Autonumber(PK)
ItemID - Integer(FK)
PieceNumber - Integer ( 1,2,3 ... etc)
MFG - String
Process - String
Length - Integer
MfgIDNum - String (ABD123XTY-1234)
I am trying to automate the data entry of the PieceNumber field. So when you enter a new PO and add items to it, once received. It will add a row to the masterlog table starting at piece number 1 through how ever many pieces we have. We number the pieces based on the items we purchased.(i.e. with Item 1 we purchased 100 pieces. So I would have Item 1 piece 1 through 100.) Then we are able to add the other data to the table. Just trying to reduce some data entry time and avoid some mistakes in the numbering of this field.
Any ideas?
Something like this would be a very simple way of doing it. You'd have to have a predefined table called Numbers with integers starting from 1 to however high a quantity you might have:
INSERT INTO MasterLog (ItemID, PieceNumber)
SELECT Item.ItemID, Numbers.Number
FROM Item, Numbers
WHERE (Item.ItemID = Forms!Items!ItemID) AND
(Numbers.Number <= Item.Quantity)
ORDER BY Numbers.Number
If you wanted to add the pieces one by one you could default the PieceNumber field on the related form. Make sure you default MasterLog.ItemID as well:
=Nz(DMax("[PieceNumber]","[MasterLog]","[ItemID] = " & Forms!Items!ItemID), 0) + 1
For a VBA solution try something like this:
Dim db As Database
Dim strSQL As String
Dim frm As Form
Dim i As Integer
Set db = CodeDb()
Set frm = Forms!Items
If frm!Quantity > 0 Then
For i = 1 To frm!Quantity
strSQL = "INSERT INTO MasterLog (ItemID, PieceNumber) " & _
"SELECT " & frm!Item & " AS ItemID, " & _
i & " AS PieceNumber"
db.Execute strSQL, dbFailOnError
Next i
End If
Set db = Nothing
All of these presume a form displaying your current item called Items.

Check if entry is already done

I am looking for a smarter way to check if a text in a record already exists.
Basically I wrote a database which stores minutes.
Since only the latest information should be stored in a textbox (there the entries are made) and "old" information goes to a history box I am looking for a way to prevent double entries in this history box.
For that reason I used the InStr Function to check if the the first 10 letters already exists in the history field it doesn't add the information. I start at position 100 since there is this header I made to differ between the entries.
Actually it works most the times but I could not figure out why it doesn't add information many times then this information is actually new so I look for a smarter way or does somebody see a mistake?
Private Sub txt_Comments_W9_LostFocus()
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' History function.
' Since only the latest information should be stored at the text field the old/other information will be stored at the history box
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim temp As String
Dim newComment As String
Dim Version As String
temp = " "
newComment = " "
Version = "<b>" & "****************************************" & "Version " & Date & ": " & "****************************************" & "</b>"
newComment = Version & txt_Comments_W9.Value
If Len(Nz(txt_History_W9.Value)) = 0 Then
If Len(Nz(txt_Comments_W9.Value)) = 0 Then
Exit Sub
Else
txt_History_W9.Value = "<div>" & newComment & "<BR>"
End If
Else
temp = txt_History_W9.Value
If InStr(100, temp, Mid(txt_Comments_W9.Value, 1, 10)) = 0 Then
txt_History_W9.Value = "<div>" & temp & newComment & "<BR>"
End If
End If
End Sub
Thanks
If Len(Nz(txt_History_W9.Value)) returns 0 you will skip the check after your else. Is that on purpose?
Also, is there a reason why you are using Mid(txt_Comments_W9.Value, 1, 10) instead of Left(txt_Comments_W9.Value, 10).
And if the arguments for Instr are as follows: InStr([start,]string1,string2[,compare]), It can return:
If string1 is "" - InStr returns 0
If string1 is Null - InStr returns Null
If string2 is "" - InStr returns start
If string2 is Null - InStr returns Null
If string2 is not found - InStr returns 0
If string2 is found within string1 - InStr returns the position at which match is found
If start > Len(string1) - InStr returns 0
Make sure you know what the InStr Function returns in case of no match.
The way this table is Structured is very odd. Your table design should ideally be something like.
tbl_MinutesHistory
------------------
minID | minDate | minEntree | minNotes
--------+---------------+---------------+--------------------------------
1 | 03/04/2014 | Paul | Meeting for Boss birthday
2 | 05/05/2014 | Eugin | Meeting to elect new boss
3 | 02/06/2014 | Francis | Company shutting down meeting
Then your history box (I guess it is a Listbox) would include everything except the last date. Rowsource would be something like
SELECT minDate, minEntree, minNotes
FROM tbl_MinutesHistory
WHERE minID Not In (SELECT Max(minID) As maxOfID FROM tbl_MinutesHistory);
You are making things more complicated with code !

function to check a text field

im using access 2007 and i need a function that will check a text field and if it found a certain word it will return a certain value according to lookup table
for example i have a text field as following :
ID Text
| 1 | engineers for mechanical work
| 2 | engineers for civil work
| 3 | engineers for electrical work
and i have lookup table as following :
Checkwords showords
| mechanical | mechanical engineer
| civil | civil engineer
| chemical | chemical engineer
| electrical | electrical engineer
| maintenance | maintenance engineer
| electronics | electronics engineer
i need the function to check the text records and if it found text like "mechanical" it will show "mechanical engineer" and if it found text like "civil" it will show "civil engineer" and so on
i have about 200 words to check so i need a function that uses a lookup table with "like" parameter ... is that possible ???
OK then, a more generic version, though be warned - the more you lean on VBA rather than SQL, the slower things get with large and even not-so-large amounts of data:
(1) Add a class module, name it LookupData, and add the following fields to it:
Public Key As String
Public Value As String
(2) In a standard module, define the following function:
Function LookupShowWords(ByVal Text)
If IsNull(Text) Then
LookupShowWords = Null
Exit Function
End If
Dim Data As LookupData
Static LookupTable As VBA.Collection
If LookupTable Is Nothing Then
Set LookupTable = New VBA.Collection
Dim RS As DAO.Recordset
Set RS = CurrentDb.OpenRecordset("LookupTable", dbOpenForwardOnly)
While Not RS.EOF
Set Data = New LookupData
Data.Key = "*" + RS(0) + "*"
Data.Value = RS(1)
LookupTable.Add Data
RS.MoveNext
Wend
End If
Dim S As String
For Each Data In LookupTable
If Text Like Data.Key Then
If Len(S) = 0 Then
S = Data.Value
Else
S = S + ";" + Data.Value
End If
End If
Next
If Len(S) = 0 Then LookupShowWords = Null Else LookupShowWords = S
End Function
(3) The query to list the results can now be rewritten to look simply like this:
SELECT ID, LookupShowWords(Text) AS ShowWords FROM MainTable ORDER BY ID;
Note that the assumption in (2) is that the lookup table is essentially static, in which case its contents can be safely cached between calls.
(1) A custom VBA function to extract the key word:
Function ExtractKeyword(ByVal Text)
Text = Mid(Text, InStr(Text, " for ") + 5)
If Right(Text, 5) = " work" Then
ExtractKeyword = Left(Text, Len(Text) - 5)
Else
ExtractKeyword = Text
End If
End Function
(2) A query to use it:
SELECT MainTable.ID, LookupTable.ShowWords
FROM MainTable LEFT JOIN
LookupTable ON ExtractKeyword(MainTable.Text) = LookupTable.CheckWords
ORDER BY MainTable.ID

MySQL or VBA Iteration Function To Find Inter-Related Records

I'm using MS Access with a MySQL database. One table has records which represent 'documents':
Table: doc
Primary Key: doc_id
These records can be linked together via a link table:
Table: link
Primary Key: link_id
Foreign Key: doc_id_A
Foreign Key: doc_id_B
Thus the records may be linked in a chain, eg Doc A linked to Doc B, which is linked to Doc C, etc, and also, Doc A may be linked to any number of other documents.
In reality, the 'family' of inter-related documents wouldn't exceed 20 records.
I'm looking for an efficient MySQL proc, or VBA function - or a query - to find all the members of a 'family' for one specified record.
Any suggestions would be most welcome!
So the link table gives a self join to doc, you absolutely need a doc to be able to:
Link to itself
... as many times as it likes
Link to another doc
... multiple times
Be separately linked to by the same other doc
... multiple times
Store information about the link
So your link table could have 10 separate links 1-1, 1-1, 1-1, 1-2, 1-2, 1-2, 2-1, 2-1, 2-1, 2-2 etc. with just 2 docs in the 'family'.
I expect when you look at this list you will probably think that you don't need most of them, a lot of the inefficiency in your solution might be coming from this unnecessary flexibility. My favoured suggestion would be to start with a strict hierarchy and build minimally from there.
But here is my answer anyway, it's tested and working in Access-2010 and local tables. ADODB should work just as well with linked tables.
Option Compare Database
Option Explicit
Const MaxInFamily = 30
'Requires a reference to "Microsoft ActiveX Data Objects 2.x Library" (VBA Menu: Tools, references)
Function GetFamily(id As Long) As Long()
Dim Found(MaxInFamily) As Long
Dim MaxFound As Integer
Dim CurrentSearch As Integer
Dim Sql As String
Dim rs As New ADODB.Recordset
Found(1) = id
MaxFound = 1
For CurrentSearch = 1 To MaxInFamily
If CurrentSearch > MaxFound Then Exit For
Sql = "SELECT doc_id_2 as NewID FROM link WHERE doc_id_1 = " & Found(CurrentSearch) _
& " AND doc_id_2 NOT IN (" & ArrayToCsv(Found, MaxFound) & ")" _
& " UNION " _
& " SELECT doc_id_1 FROM link WHERE doc_id_2 = " & Found(CurrentSearch) _
& " AND doc_id_1 NOT IN (" & ArrayToCsv(Found, MaxFound) & ")"
rs.Open Sql, CurrentProject.Connection
Do While Not rs.EOF
MaxFound = MaxFound + 1
Found(MaxFound) = rs("NewID").Value
rs.MoveNext
Loop
rs.Close
Next CurrentSearch
GetFamily = Found
End Function
Function ArrayToCsv(SourceArray() As Long, ItemCount As Integer) As String
Dim Csv As String
Dim ArrayIndex As Integer
For ArrayIndex = 1 To ItemCount
Csv = Csv & SourceArray(ArrayIndex)
If ArrayIndex < ItemCount Then Csv = Csv & ", "
Next ArrayIndex
ArrayToCsv = Csv
End Function
Duplicated results and queries are avoided by excluding items already found at the server, and as the link is unidirectional I've used a UNION query to look both ways at once. At most it'll do MaxInFamily round trips to the server.
Unfortunately, it cannot be done in MySQL given such a table structure as MySQL does not support recursive queries. I suggest to explore the answers in Mysql recursion? - there is some advice as to how to store the data in MySQL to be able to write such queries.
You also have three other options:
In case you know the max. depth of such families, and it's not huge, you can still achieve it with mySQL. For a depth of 3 levels, it would look like this:
SELECT A.doc_id_a as a, B.doc_id_a a_child, C.doc_id_a as a_sub_child
FROM links as A, links as B, links as C
WHERE A.doc_id_a = your_doc_id AND
A.doc_id_b = B.doc_id_a AND
B.doc_id_b = C.doc_id_a
Following the same logic, you can add as many layers as you need. The only thing is - you'll need to get result from all columns and find unique values (and there can be many rows, if the relationship is not 1-1 all the time).
A second option is to do this in VBA. I do not know enough to provide the code, but essentially it could look like this (a recursive approach):
family = array();
family = getFamily('your_doc_id', family);
function getFamily(id) {
children = DB->getColumn('SELECT doc_id_b FROM links WHERE doc_id_a = ?', id);
if (empty(children)) return family;
else {
foreach (children as child) {
family[] = getFamily(child);
}
}
}
Lastly, you can switch to PostgreSQL, which supports recursive queries :)
(http://www.postgresql.org/docs/8.4/static/queries-with.html).
I had a similar problem some time ago. Instead of creating links between the documents I used a separate table containing the families:
Table: LinkTable
PK: LinkId, DocumentID
FK: DocumentID
Instead of having pairs of document IDs, each time you create a new family, you introduce a new LinkId.
LinkId | DocumentID
1 | 7
1 | 9
1 | 13
2 | 4
2 | 22
2 | 23
2 | 30
3 | 6
3 | 80
Here you have three families with the document IDs:
{ 7, 9, 13 },
{ 4, 22, 23, 30 },
{ 6, 80 }
It is easier to retrieve families, however it requires a somewhat more complicated logic for inserting and removing documents to and from families.