I'm using the below mentioned code to select a record ID from an Access Database that wasn't already selected in the last day and add it to an array.
The general goal is that a record that matches the initial "Difficulty" criteria will be retrieved so long as either the record was never selected before OR the record wasn't chosen in the last 2 days. After the loop is done, I should have x amount of unique record ID's and add those onto an array for processing elsewhere.
Private Function RetrieveQuestionID(questionCount As Integer)
' We're using this retrieve the question id's from the database that fit our arrangements.
Dim intQuestArray(0 To questionCount) As Integer
Dim QuestionConnection As New OleDb.OleDbConnection("PROVIDER=Microsoft.ACE.OLEDB.12.0;Data Source = |DataDirectory|\Database\MillionaireDB.accdb;")
QuestionConnection.Open()
For i As Integer = 1 To intNoOfQuestions
'TODO: If there are no valid questions, pull up any of them that meets the difficulty requirement....
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())) ORDER BY Rnd((Questions.QuestionID) * Time());", QuestionConnection)
QuestionConnectionQuery.Parameters.AddWithValue("?", intQuestionDifficulty(i - 1).ToString)
Dim QuestionDataAdapter As New OleDb.OleDbDataAdapter(QuestionConnectionQuery)
Dim QuestionDataSet As New DataSet
QuestionDataAdapter.Fill(QuestionDataSet, "Questions")
intQuestArray(i - 1) = QuestionDataSet.Tables("Questions").Rows(0).Item(0)
Dim QuestionConnectionUpdateQuery As New OleDb.OleDbCommand("UPDATE Questions SET Questions.LastDateRevealed = NOW() WHERE Questions.QuestionID = [?]", QuestionConnection)
QuestionConnectionUpdateQuery.Parameters.AddWithValue("?", intQuestArray(i - 1).ToString)
QuestionConnectionUpdateQuery.ExecuteNonQuery()
Next
QuestionConnection.Close()
Return intQuestArray
End Function
However, looping through the array will show that there are records are somehow being repeated even though the record updates during the loop.
Is there another way to loop through the database and pull up these records? I even attempted to move the .Open() and .Close() statements to within the For...Next loop and I'm given worse results than before.
As Steve wrote, the >= should be a < .
In addition, your WHERE clause is missing parentheses around the OR part.
It should be (without all unnecessary parentheses):
SELECT Questions.QuestionID
FROM Questions
WHERE Questions.QuestionDifficulty=[?]
AND ( Questions.LastDateRevealed Is Null
OR Questions.LastDateRevealed < DateAdd('d',-2,Date()) )
ORDER BY Rnd(Questions.QuestionID * Time());
Also have a look at How to get random record from MS Access database - it is suggested to use a negative value as parameter for Rnd().
Related
I am creating a library management system. I want to generate different book code for different book genre. For eg:- There is a combo box with book three book genres(novel,literature,poem) and there is another text box with book code. I want if someone chooses novel in comboBox, book code starts form N001 and if someone chooses literature in comboBox, book code starts from L001 and if someone chooses poem, book code starts from P001.
(My vb application is connected with ms access database.)
I see several problems with your fundamental numbering strategy.
Most library numbering systems (Dewey Decimal/Library of Congress) factor sort ordering abilities to their numbering system and allow for future additions to be able to sort higher without needing to shift the number of all other items. With your incremental strategy, how would you accommodate inserting a book later in the process?
If you set up with three digits of numbering, what would you do once you get 1000 books in a category?
While you could use a magic number table that includes the last id allocated per category and increment it as you add a new book. If you have more than one user, there is a potential for a race condition where two users get the same last number and both try to increment it at the same time landing on the same new Id.
I typically recommend keeping a generic ID that has no meaning in the system. This number would be set as an Auto Number in Access and the database would generate new Ids when records are added and ensure that you don't have the collision issue above. If you absolutely need a publically viewable Id, you can use the magic number strategy for a separate BookNumber column that isn't used for record identity. That way if you have a collision in the future, data won't get corrupted.
there are several schools of thought on how one can approach this.
One way? Well, you say have a book type or some such, and then you have that book code. It would be a simple number column without the prefix (P, B etc.).
So, to get the next number, you would then go:
myBookNumber = DMAX("BookNumber","tblBooks","BookType = 'P')
MyBookNumber = MyBookNumber + 1
So, you simply store the book number separate from the prefix. In forms, reports etc., you can then always display the book number like rst!BookType & rst!BookNumber
Another way, which I prefer is to create a table that allows you to get the next book number This also makes it easy to add more types, and better is such a design lets you set the starting number. This approach is also often used for invoice numbers.
So, you create a table called tlbBookIncrements. It will look like this:
ID: (pk - autonumber)
BookType: text (the one char book type)
BookNumber: Number the next book number to assign.
You then build a function like this:
Public Function GetNextBookN(strBookType As String) As Long
Dim strSQL As String
Dim rst As DAO.Recordset
strSQL = "SELECT * FROM tblBookIncrement where BookType = '" & strBookType & "'"
Set rst = CurrentDb.OpenRecordset(strSQL)
If rst.RecordCount = 0 Then
' this book type does not exist - lets add it and start at 1
rst.AddNew
rst!BookType = strBookType
rst!booknumber = 1
End If
GetNextBookN = rst!booknumber
rst!booknumber = rst!booknumber + 1
rst.Update
rst.Close
End Function
Update:
The poster was using vb.net, not VBA, my bad.
Here is the above as vb.net, it really much the same.
Public Function GetNextBookN(strBookType As String) As Integer
Using mycon As New OleDbConnection(My.Settings.ConnectionString1)
Dim strSQL As String
strSQL = "SELECT * FROM tblBookIncrement where BookType = '" & strBookType & "'"
Using da As New OleDbDataAdapter(strSQL, mycon)
Dim cmdBuilder = New OleDbCommandBuilder(da)
Dim rst As New DataTable
da.Fill(rst)
da.AcceptChangesDuringUpdate = True
With rst
If .Rows.Count = 0 Then
With .Rows.Add
.Item("BookType") = strBookType
.Item("booknumber") = 1
End With
GetNextBookN = 1
Else
GetNextBookN = .Rows(0).Item("booknumber")
.Rows(0).Item("booknumber") += 1
End If
End With
da.Update(rst)
End Using
End Using
End Function
So, now anytime you need a book number, you can go like this:
Say, in a form, you need to assign the book number, you could go like this:
You could say use the on-insert event of the form, and go like this:
me!BookNumber = me!bookType & GetNextBookN(me!bookType)
So, the above would say if the bookType is "P", result in P101 if the numbering table has a book type of P and the booknumber set to 101. And of course once you pull that number, then the handy dandy function will increment the number for you.
And I also added the feature that if the type requested does not exist, then we add the type and start at 1. This way, the code works as you over time add new types of books, and then the book number table will always work, and have available a next book number easily available in code.
So i am making a voting program but i am stuck right now.
I have searched for solutions and i have followed them precisely but still no result.
Basically i want to count the total occurrence for each specific record from access in visual basic.
For example i have the candidate number 1 voted by three persons and candidate 2 by 7 persons, i want to show this voting result in a textbox but somehow it always shows me the wrong number
So here is my code:
Dim TotalVotes As Integer
myCommand = New OleDbCommand("SELECT CandidateNumber, COUNT (*) FROM Student_Voting GROUP BY CandidateNumber", dbconn)
TotalVotes = myCommand.ExecuteScalar
NovTextBox.Text = TotalVotes
myCommand.Dispose()
myReader.Close()
This query here gives a result of the first candidate number not the total votes for selected candidate number:
SELECT CandidateNumber, COUNT (*) FROM Student_Voting GROUP BY CandidateNumber
I have tried this too but still wrong result:
SELECT COUNT(CandidateNumber) AS NoVotes FROM Student_Voting GROUP BY CandidateNumber
I don't know what's the problem here, it's suppose to be simple but yet.
If anyone could help i'd very much appreciate it.
Thanks in advance
First of all, the initial query you're running would return more than one record if you had more than one candidate. ExecuteScaler returns only the first column of the first row.
The question is, do you want all of the records or do you just want one person's record? If you just want one person's record you'll need add a WHERE clause to your sql statement to specify on that candidate.
If you want all of the records it would look something like this:
Using myCommand = New OleDbCommand("SELECT CandidateNumber, COUNT (*) AS CountValue FROM Student_Voting GROUP BY CandidateNumber", dbconn)
Using dr = myCommand.ExecuteReader
' Loops over all the canidate counts one by one.
While dr.Read
Dim totalVotes As Integer = CInt(dr("CountValue"))
Dim candidateNumber As String = dr("CandidateNumber")
End While
End Using
End Using
If you want only one record then you can use ExecuteScaler something like this:
Using myCommand = New OleDbCommand("SELECT COUNT (*) AS CountValue FROM Student_Voting WHERE CandidateNumber = #CandidateNumber GROUP BY CandidateNumber", dbconn)
myCommand.Parameters.Add("#CandidateNumber", OleDbType.VarChar).Value = "1"
Dim totalVotes = CInt(myCommand.ExecuteScalar)
End Using
I don't know the actual types in your database so you would need to tweak the OleDbType to fit your table setup and perhaps some of the casts.
I have a project where I connected an Access Database through a DataGridView. I've made some queries based on info inputed by the user through textboxes and comboboxes. Now I need to find a way to count the Average of the records found after the query from one specific column. Is there a way to do that ?
Store the counts from your queries...
Dim lstCounts As New List(Of Integer)
'Your database retrieval method: SELECT COUNT(*) FROM table WHERE field = 'Blah'
lstCounts.Add(<above result>)
'Your database retrieval method: SELECT COUNT(*) FROM table WHERE field = 'Blah1'
lstCounts.Add(<above result>)
'Your database retrieval method: SELECT COUNT(*) FROM table WHERE field = 'Blah2'
lstCounts.Add(<above result>)
'etc.
Find the average...
Dim nTotal As Integer = 0
Dim dAverage As Decimal = 0.0
For i As Integer = 0 to lstCounts.Count - 1
nTotal += lstCounts(i)
Next
'Make sure you aren't dividing by zero
If lstCounts.Count > 0
dAverage = nTotal / lstCounts.Count
End If
You could also simply just add the total as you perform each query and not bother using a List, but then you need to track how many queries you ran.
I don't know what i doing wrong. I have been on several forums trying to figure out how to sort a table in visual basic.
I have treid with and with out a dataview, but noting seams to work.
I have a logg that the user can do new inserts in. It has 3 columns. "Date", "Tool", "Comment".
When my VB application loads the program reads the table from a Access database and i get my sorting just fine by the sql phrase:
"select * from Logg ORDER BY Logg.Datum DESC"
After a user have uppdated the table i whant to sort it again. I have treid the following, but nothing happens. The order is the same whatever i do.
DS is my Dataset and dw my dataview, and "Datum" the column i whant to sort
DS.Tables("hela").DefaultView.Sort = "Datum DESC"
dw = DS.Tables("hela").DefaultView
For i = 0 To antal_poss - 1
LOGG(i, 0) = dw.Table.Rows(i).Item(3)
LOGG(i, 1) = dw.Table.Rows(i).Item(1)
LOGG(i, 2) = dw.Table.Rows(i).Item(4)
Next i
What am i doing wrong?
In your code you use the DataView to retrieve the Table and then the DataRows, but you extract them following the order on the DataTable.
You need to loop following the order of the DataView.
Something like this
Dim i As Integer = 0
For Each row As DataRowView in dw
LOGG(i, 0) = row.Item(3)
LOGG(i, 1) = row.Item(1)
LOGG(i, 2) = row.Item(4)
i += 1
Next i
Of course this assume that your LOGG array contains enough entries to accomodate every row retrieved. It is the same number of the rows in the DataTable
I have a data adapter with 4 tables in a dataset. When I update a new record to the table it appears in the SQL database and the associated datagridivew has been reloaded with the dat in the table, but when I try and read the new record using the following code it can't find the record.
Dim row As DataRow = dsSrvAV.Tables("ServiceAvailability").Select("ID = " & intRecordID).FirstOrDefault()
The same code is used to read other records that were in the database when the application opened, it's just new records that it can't read.
This is the code that writes the new records
Dim newAvailability As DataRow = dsSrvAV.Tables("ServiceAvailability").NewRow()
'Add some data to it
newAvailability("Service_ID") = cboServices.SelectedValue
newAvailability("Date") = Format(dtpDate.Value.ToString, "Short Date")
newAvailability("Downtime") = nudDowntime.Value
newAvailability("Notes") = txtNotes.Text
newAvailability("MajorIncident") = txtMajorIncident.Text
newAvailability("ActionsTaken") = txtActionsTaken.Text
newAvailability("Type") = cboType.SelectedValue
newAvailability("Root_Cause") = txtRootCause.Text
'Add it to the table
dsSrvAV.Tables("ServiceAvailability").Rows.Add(newAvailability)
'Update the adapter
daSrvAv.Update(dsSrvAV, "ServiceAvailability")
dsSrvAV.Tables("ServiceAvailability").AcceptChanges()
Can anyone offer any thoughts as to why this won't allow new records to be read back.
Thanks
Rich
Per comments - this solved the issue.
Close your dsSrvAv dataset, and then re-open it, and then do the select.
Regardng performance: are you adding 1 record per second of 1,000,000. If its 1,000,000 then yes there's an overhead. If its 1 per second there isn't any noticable overhead.