How to keep incrementing the same value? - vb.net

Try
Dim count as Int64
Using cm As New SQLiteCommand("SELECT COUNT([RollNo]) FROM [StudentTbl]", cn)
If Not cm.ExecuteScalar() Is DBNull.Value Then
count = Convert.ToInt64(cm.ExecuteScalar())
Else
count = 0
End If
End Using
Catch ex As Exception
MsgBox(ex.Message)
Return
End Try
txtRollNo.Text = count +1

Move the count declaration outside the Try block otherwise it will not be visible outside the block. You attempt to use it outside the block.
You cannot assign a number to a .Text property. It requires a String. Convert count + 1 to a String.
Keep your database objects local so you can control that they are closed and disposed. This is particulary important for connections which are precious resources.
Why are you executing your query twice?
In most SQL languages Count will not return null. It will return 0 if there are no rows the match criteria.
It pains me to think that you have a Class level variable that is an Open connection. Get rid of it if you do. In my code you must open the connection before executing the command.
Good job converting the return value of the ExecuteScalar. Also good job passing the command text and the connection to the constructor of the command.
I am a bit leery of why you want this number. If you are expecting to use count +1 for the next primary key -- DON'T. If this is a multi-user environment then it will not work. Even if it is single user, suppose you have deleted a few records. Your method will give you a duplicate Primary Key. Set your RollNo field to auto-increment/identity and the database will do it for you.
Private Sub GetCount()
Dim count As Int64
Try
Using cn As New SQLiteConnection("Your connection string")
Using cm As SQLiteCommand = New SQLiteCommand("SELECT COUNT([RollNo]) FROM [StudentTbl]", cn)
cn.Open()
count = Convert.ToInt64(cm.ExecuteScalar())
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message)
Return
End Try
txtRollNo.Text = CStr(count + 1)
End Sub

Related

Clone a datarow into same table but primary key creatin error

I'd like to allow the user to clone a record. Data is from MS SQL db and is contained in a single table with a primary key (int32) autoincrementing titled "CriteriaID". I get a CriteraID can't be null error which I would expect since it's an ID column. I tried setting it to allowdbnull in on the dataset side but no luck. How do I set it to the value needed to eventually save it into DB? If I hit new record that column is -1,-2,-3, etc. but in this case I want to clone all columns EXCEPT for the CriteriaID. I also tried NOTHING, VBNULL, and a random integer not already in DB. WOuld it be easier to use a different method?
Try
If Not IsNothing(TCriteriaBindingSource.DataSource) Then
Dim sr As DataRow() = DsCriteria.Tables("tCriteria").Select("CriteriaID = " & DsCriteria.Tables("tCriteria").Rows(TCriteriaBindingNavigator.BindingSource.Position).Item("CriteriaID"))
sr(0).Item("CriteriaID") = DBNull.Value
DsCriteria.Tables("tCriteria").NewRow()
DsCriteria.Tables("tCriteria").Rows.Add(sr(0))
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try

Getting "Database is Locked" when trying to move a list of records from one table to another table in SQLite

I have a Public Sub to move a collection of records from one table to another in the same SQLite database. First it reads a record from strFromTable, then writes it to strToTable, then deletes the record from strFromTable. To speed things up, I've loaded the entire collection of records into a transaction. When the list involves moving a lot of image blobs, the db gets backed up, and throws the exception "The Database is Locked". I think what is happening is that it's not finished writing one record before it starts trying to write the next record. Since SQLite only allows one write at a time, it thows the "Locked" exception.
Here is the code that triggers the error when moving a lot of image blobs:
Using SQLconnect = New SQLiteConnection(strDbConnectionString)
SQLconnect.Open()
Using tr = SQLconnect.BeginTransaction()
Using SQLcommand = SQLconnect.CreateCommand
For Each itm As ListViewItem In lvcollection
SQLcommand.CommandText = $"INSERT INTO {strToTable} SELECT * FROM {strFromTable} WHERE id = {itm.Tag}; DELETE FROM {strFromTable} WHERE ID = {itm.Tag};"
SQLcommand.ExecuteNonQuery()
Next
End Using
tr.Commit()
End Using
End Using
When I get rid of the transaction, it executes without error:
Using SQLconnect = New SQLiteConnection(strDbConnectionString)
SQLconnect.Open()
Using SQLcommand = SQLconnect.CreateCommand
For Each itm As ListViewItem In lvcollection
SQLcommand.CommandText = $"INSERT INTO {strToTable} SELECT * FROM {strFromTable} WHERE id = {itm.Tag}; DELETE FROM {strFromTable} WHERE ID = {itm.Tag};"
SQLcommand.ExecuteNonQuery()
Next
End Using
End Using
I'm not very good with DB operations, so I'm sure there is something that needs improvement. Is there a way to make SQLite completely finish the previous INSERT before executing the next INSERT? How can I change my code to allow using a transaction?
Thank you for your help.
.
Ok ... here is the solution that I decided to go with. I hope this helps someone finding this in a search:
Dim arrIds(lvcollection.Count - 1) As String
Dim i as Integer = 0
' Load the array with all the Tags in the listViewCollection
For i = 0 to lvcollection.Count - 1
arrIds(i) = lvcollection(i).Tag 'item.Tag holds the Primary Key "id" field in the DB
Next
'build a comma-space separated string of all ids from the array of ids.
Dim strIds as String = String.Join(", ", arrIds)
Using SQLconnect = New SQLiteConnection(strDbConnectionString)
SQLconnect.Open()
Using tr = SQLconnect.BeginTransaction()
Using SQLcommand = SQLconnect.CreateCommand
SQLcommand.CommandText = $"INSERT INTO {strToTable} SELECT * FROM {strFromTable} WHERE id IN ({strIds});"
SQLcommand.ExecuteNonQuery()
SQLcommand.CommandText = $"DELETE FROM {strFromTable} WHERE ID IN ({strIds});"
SQLcommand.ExecuteNonQuery()
End Using
tr.Commit()
End Using
End Using
The IN statement allows me to pass all of the "id" values to be deleted as a batch. This solution is faster and more secure than doing them one by one with no transaction.
Thanks for the comments, and best wishes to everyone in their coding.

How do I retrieve a value from an SQL query and store it in a variable in VB.NET?

I am trying to find the max product ID and store the value in a local variable "MaxID" and return this value. I am trying to convert the result of the query into an Integer type but I am not able to do it. Below is the code:
Public Function GetMaxID(ByVal TableName As String, ByVal ID As String) As Integer
Dim MaxID As Integer
Dim sqlquery As SqlCommand
Dim field_name As String = ID
Dim con As SqlConnection
con = New SqlConnection()
con.ConnectionString = "Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename='D:\Docs Dump\Work\Srinath\SrinathDB.mdf';Integrated Security=True;Connect Timeout=30"
con.Open()
Try
sqlquery = New SqlCommand("SELECT MAX( #field ) FROM #table ", con)
sqlquery.Parameters.AddWithValue("#field", field_name)
sqlquery.Parameters.AddWithValue("#table", TableName)
MaxID = CInt(sqlquery.ToString)
con.Close()
Return MaxID
Catch ex As Exception
Return 0
Exit Function
con.Close()
End Try
End Function
End Class
MaxID = CInt(sqlquery.ExecuteScalar())
You also should know about SqlCommand.ExecuteReader(), SqlCommand.ExecuteNonQuery() (for inserts/updates/deletes), and SqlDataAdapter.Fill().
Where you'll still have a problem is you can't use a parameter value for the table name or column name. The Sql Server engine has a "compile" step, where it has to be able to work out an execution plan, including permissions/security, at the beginning of the query, but variable names like #table and #field aren't resolved until later. It's not what actually happens, but think of it as if you had string literals in those places; imagine trying to run this:
SELECT MAX('ID') FROM 'MyTable'
MAX('ID') will always return the string value ID, and not anything from an ID column in any rows. But the MyTable part is not the correct place for a string literal, and such a query wouldn't even compile.
I also see people here from time to time try to create functions like GetMaxId(), and it's almost always misguided in the first place. If the intended use for this function is the same as what I usually see, you're setting up a major race condition issue in your application (one that probably won't show up in any testing, too). Sql Server gives you features like identity columns, sequences, and the scope_identity() function. You should be using those in such a way that new IDs are resolved on the server as they are created, and only (and immediately) then returned to your application code.
But that issue aside, here's a better way to structure this function:
Public Class DB
Private conString As String = "Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename='D:\Docs Dump\Work\Srinath\SrinathDB.mdf';Integrated Security=True;Connect Timeout=30"
'You want a separate method per-table that already knows the table and column names
Public Function GetMyTableMaxID() As Integer
Dim sql As String = "SELECT MAX(ID) FROM MyTable"
Using con As New SqlConnection(conString), _
sqlQuery As New SqlCommand(sql, con)
'Parameters would go here.
'Do NOT use AddWithValue()! It creates performance issues.
' Instead, use an Add() overload where you provide specific type information.
'No exception handling at this level. The UI or business layers are more equipped to deal with them
con.Open()
Return CInt(sqlQuery.ExecuteScalar())
End Using
'No need to call con.Close()
'It was completely missed in the old code, but handled by the Using block here
End Function
End Class

how to clear the previous search from the textbox?

I am new in vb.net. i have a database and i am running a search query of employee records when search button clicked and display that information in textbox, however when a user is not in the database, the search output is displaying the information of the previous search. the information in textbox should display blank or say "No record found" if the user is not in the record. not sure what is wrong in my code.
Try
myConnection.Open()
Dim str As String
str = "SELECT * FROM tblEmp WHERE (EmpID = '" & ADS.UserEmpID & "')"
Dim cmd As OleDbCommand = New OleDbCommand(str, myConnection)
dr = cmd.ExecuteReader
While dr.Read
If dr.HasRows > 0 Then
MessageBox.Show("user already in the system", "Warning", MessageBoxButtons.OK)
ElseIf dr.HasRows = 0 Then
MessageBox.Show("Not Onboarded", "Warning", MessageBoxButtons.OK)
End If
BGC1 = dr("PreStartChecks").ToString
BGC2 = dr("EmpName").ToString
myConnection.Close()
End While
Catch ex As Exception
MsgBox("Unable to Connect to BGC DB. You may not have access or DB not available." &
ex.ToString)
End Try
Your While loop and If statement don't make sense. Firstly, HasRows is type Boolean so testing whether it is greater than zero is nonsensical. Secondly, Read returns False if there are no rows so the only way you can get to that If statement is if there is at least one row to read so testing HasRows when you already know that there are rows is also nonsensical. The proper option here is to use just an If statement and test Read only:
If dr.Read() Then
'There is a row to read and it was just read, so you can now get the data from the reader.
Else
'There is no row to read.
End If
If you want to clear a control when there's no data, you do so in the Else block.
The "rules" about when and how to use HasRows and Read are very simple and logical:
If all you care about is whether the query result set contains data or not but you don't care what that data is, just use an If statement to test HasRows. The HasRows property is type Boolean so there's no need to compare it to anything. It already is True or False.
If there can only be zero or one row in the result set, just use an If statement to call Read and test the result. Again, it's type Boolean so there's no need to compare it to anything. If it returns True then you can access the data for the row you just read.
If there can be multiple rows and you don't want to do anything special if there are no rows then just use a While or Do While loop to call Read and access the row that was just read inside the loop.
If there can be multiple rows and you do want to do something special if there are no rows, use an If statement to test HasRows and then a While or Do While loop inside the If block to call Read. You would handle the case where there are no rows in the Else block.
Assuming that txtBGC1 and txtBGC2 are TextBoxes, you could do something like this, assuming that the query can at most return one employee
...
If dr.Read Then ' There is an employee
txtBGC1.Text = dr("PreStartChecks").ToString
txtBGC2.Text = dr("EmpName").ToString
Else ' There is no employee
txtBGC1.Text = ""
txtBGC2.Text = "No record found"
End If
myConnection.Close()

Updating a record in MS Access is not working with SQL Syntax

Could you please help me with this.
I have these codes..
Private Sub dgvInTraining_CellClick(sender As Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvInTraining.CellClick
If e.ColumnIndex = 0 Then
Dim transID As Integer = Me.dgvInTraining.Rows(e.RowIndex).Cells(1).Value
UPdateInTraining(transID, Now)
Else
Exit Sub
End If
End If
End Sub
Public Sub UPdateInTraining(transID, timeOut)
Try
cnn.Open()
query = "UPDATE InTraining SET TimeOut = #timeOut WHERE TransID = #transID"
cmd = New OleDbCommand(query, cnn)
cmd.Parameters.AddWithValue("#transID", transID)
cmd.Parameters.AddWithValue("#timeOut", timeOut)
cmd.ExecuteNonQuery()
Catch ex As Exception
GetErrorMessage(ex)
Finally
CloseConnection()
End Try
End Sub
Can you please tell me what I'm doing wrong. I am able to save just fine but when I try to update the record I created, it doesn't change the values in the database. My database definition follows:
TransID AutoNumber
ID Text
TimeIn Date/Time
TimeOut Date/Time
WithWater Yes/No
TransDate Date/TIme
OleDB simply uses parameters as placeholders (the names do not matter/are ignored), so you have to take care to add them in the same exact order as they appear in the SQL. Your SQL uses the order #timeOut then #transID:
"UPDATE InTraining SET TimeOut = #timeOut WHERE TransID = #transID"
But you are adding them in the opposite order:
cmd.Parameters.AddWithValue("#transID", transID)
cmd.Parameters.AddWithValue("#timeOut", timeOut)
It will be looking for a TransID of whatever the timeout value is. Swap those lines and it should work barring any other issues.
Note that MSDN suggests using "?" as a placeholder1. Doing so will force you to look back at the SQL to see which to add next. But using ? will not fix adding them in the wrong order.
Especially when there are several parameters, I prefer to use "#p1, #p2..." style parameters. The numeral helps index the column names in the SQL and you can visually see that you added them in the right order:
cmd.Parameters.AddWithValue("#p1", strBar)
cmd.Parameters.AddWithValue("#p2", nFoo)
1. In fact they say ? must be used. This is not true, it just does not map values to names but relies on the order added.