I am deleting from an sqlite database using the ids of the records like this
(the dirID is an array of the IDs):
Dim i As Integer = 0
Dim conn As New SQLiteConnection("Data Source=" & DBPath)
Dim cmd As New SQLiteCommand("DELETE FROM directory WHERE id IN (#ID)", conn)
cmd.Parameters.AddWithValue("#ID", Join(dirID, ","))
'conn.SetPassword(dbPassword)
conn.Open()
Try
mytransaction = conn.BeginTransaction()
'// delete directory //
If dirID IsNot Nothing Then
cmd.ExecuteNonQuery()
End If
mytransaction.Commit()
conn.Close()
Catch ex As Exception
mytransaction.Rollback()
strLastError = ex.Message
Debug.Print(strLastError)
Finally
cmd.Dispose()
conn.Dispose()
End Try
The problem is that it doesn't always delete from the database, and its not throwing any errors.
Could there be a better way of deleting?
That's not how parameters work.
If your IN list is 1, 2, 3, the command is trying to delete the record where ID equals "1, 2, 3", which is none. So no error is thrown and no record is deleted, because none was found. Thus, you will also find out that your code only works when your list contains 1 item.
Solution: You have to build your delete query (string manipulation), instead of working with parameters. Just beware of SQL Injection.
Update
From your code, it would be something like that:
Dim delcmd = "DELETE FROM directory WHERE id IN (" + Join(dirID, ",") + ")"
Dim cmd As New SQLiteCommand(delcmd, conn)
And no parameter call. Beware: I just tweaked your code, but it is not SQL Injection safe. You should read about that and about what's been written on the subject here in StackOverflow.
Related
I am Inserting the data-table into SQLite Database. I am doing like this.
First I Fetch the data with getdata function and insert it into datatable, then with For Each Loop i made the Insert Command and Execute It. I am having 50000 Records it will take 30 Minutes to run.
Please Guide the suitable approach. Here is the Code.
Dim xtable As DataTable = getdata("select * from tablename")
Dim str As String = Nothing
For Each r As DataRow In xtable.Rows ''''HERE IT WILL TAKE TOO MUCH TIME
str = str & ("insert into tablename values(" & r.Item("srno") & "," & r.Item("name"));")
Next
EXECUTEcmd(str)
Public Function getdata(ByVal Query As String) As DataTable
connectionString()
Try
Dim mds As New DataTable
Dim mycommand As New SQLiteCommand(DBConn)
mycommand.CommandText = Query
Dim reader As SQLiteDataReader = mycommand.ExecuteReader()
mds.Load(reader)
Return mds
Catch ex As Exception
MsgBox("DB Error", vbCritical, "")
MsgBox(Err.Description)
Return Nothing
End Try
End Function
Public Sub EXECUTEcmd(ByVal selectcmd As String)
Using cn = New SQLiteConnection(conectionString)
cn.Open()
Using transaction = cn.BeginTransaction()
Using cmd = cn.CreateCommand()
cmd.CommandText = selectcmd
cmd.ExecuteNonQuery()
End Using
transaction.Commit()
End Using
cn.Close()
End Using
End Sub
here the Conncection String is:
conStr = "Data Source=" & dbpath & ";Version=3;Compress=True; UTF8Encoding=True; PRAGMA journal_mode=WAL; cache=shared;"
Use a stringbuilder to build your string, not string concatenation
Dim strB As StringBuilder = New StringBuilder(100 * 50000)
For Each r As DataRow In xtable.Rows
strB.AppendLine($"insert into tablename values({r.Item("srno")},{r.Item("name")});")
Next
Strings cannot be changed in .net. Every time you make a new string VB has to copy everything out of the old string into a new one and add the new bit you want. If each of your insert statements is 100 bytes, that means it copies 100 bytes, then adds 100, then copies 200 bytes and adds 100, then copies 300 bytes, then 400 bytes, then 500 bytes. By the time it has done 10 strings it has made 5.5 kilobytes of copying. By the time it's done 50 thousand strings it has copied 125 gigabytes of data. No wonder it's slow!
Always use a StringBuilder to build massive strings
--
I'm willing to overlook the sql injection hacking nag for this one, because of the nature of the task, but please read http://bobby-tables.com - you should never, ever concatenate values into an SQL as a way of making an sql that has some varying effect.
This entire exercise would be better done as this (pseudocode) kind of thing:
Dim sel as New SQLiteCommand("SELECT a, b FROM table", conn)
Dim ins as New SQLiteCommand("INSERT INTO table VALUES(:a, :b)", conn)
ins.Parameters.Add("a" ...)
ins.Parameters.Add("b" ...)
Dim r = sel.ExecuteReader()
While r.Read()
ins.Parameters("a") = r.GetString(0)
ins.Parameters("b") = r.GetString(1)
ins.ExecuteNonQuery()
End While
That is to say, you minimize your memory by reading rows one at a time out of ther edaer and inserting them one at a time in the insert; the insert command is prepared once, you just change the parameter values, execute it, change them again, execute it ... It's what parameterized queries were designed for (as well as stopping your app getting hacked when someone puts SQL in your variable, or even just stopping it crashing when you have an person named O'Grady
Maybe you must refactor your code like this:
Dim xtable As DataTable = getdata("select * from tablename")
Using cn = New SQLiteConnection(conectionString)
cn.Open()
Using transaction = cn.BeginTransaction()
Try
Using cmd = cn.CreateCommand()
cmd.Transaction = transaction
For Each r As DataRow In xtable.Rows ''''HERE IT WILL TAKE TOO MUCH TIME
cmd.CommandText = "insert into tablename values(" & r.Item("srno") & "," & r.Item("name") & ")"
cmd.ExecuteNonQuery()
Next
End Using
transaction.Commit()
Catch ex As Exception
transaction.Rollback()
End Try
End Using
End Using
Public Function getdata(ByVal Query As String) As DataTable
connectionString()
Try
Dim mds As New DataTable
Dim mycommand As New SQLiteCommand(DBConn)
mycommand.CommandText = Query
Dim reader As SQLiteDataReader = mycommand.ExecuteReader()
mds.Load(reader)
Return mds
Catch ex As Exception
MsgBox("DB Error", vbCritical, "")
MsgBox(Err.Description)
Return Nothing
End Try
End Function
Instead of concatenate an possible giant string, wrap all your inserts into a single transaction, like above. This will reduce the memory used and also make sqlite perform faster.
Please help to get the primary key of the last insert record as the one which i have gives me duplicate rows in the database and return 0
Try
If Conn.State = ConnectionState.Open Then Conn.Close()
'insert the new customer data
Conn.Open()
cmd = New SqlCommand("insert into Quote values ('" & dateOFCreat & "','" & Emp & "','" & Customer_no & "' )", Conn)
Dim a As Integer = cmd.ExecuteNonQuery()
Dim results As Integer
Dim cmd_results As SqlCommand
'Get the last created Quote in the Database
cmd_results = New SqlCommand("Select ##Identity from Quote", Conn)
results = cmd.ExecuteScalar
TxtLastQuoteID.Text = results
If a = 0 Then
MsgBox("Error")
End If
Conn.Close()
Catch ex As Exception
MsgBox(ex.Message)
End Try
You can make use of the batch commands supported by Sql Server. Just put together the two instructions and just use ExecuteScalar. However, before that, you need to fix ASAP your Sql Injection vulnerability. Do not concatenate strings to build an sql command, but use parameters.
Try
Using con as SqlConnection = new SqlConnection(....constringhere...)
Conn.Open()
Dim sqlText = "insert into Quote values (#dat,#emp,#cusno); SELECT SCOPE_IDENTITY()"
cmd = New SqlCommand(sqlText,Conn)
cmd.Parameters.Add("#dat", SqlDbType.NVarChar).Value = dateOFCreat
cmd.Parameters.Add("#end", SqlDbType.NVarChar).Value = emp
cmd.Parameters.Add("#cusno", SqlDbType.NVarChar).Value = Customer_no
Dim lastID As Integer = cmd.ExecuteScalar()
TxtLastQuoteID.Text = lastID.ToString()
Conn.Close()
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Notice also that is a very bad thing to keep a global connection object. You don't need that because ADO.NET implements Connection Pooling that makes opening a connection a very fast operation. Instead keeping a connection around requires a lot of effort to work correctly around it
Finally you can look here to better understand the difference between SCOPE_IDENTITY and ##IDENTITY and why is usually better to use the first one.
I cant delete rows in a table
it doesn't come up with any error messages
con has been used on other statements and works just fine
textbox1 is read-only and gets input from a listbox filled with the values of the "Driver" column
and check() is just to refresh the listbox containing the values
con.Open()
Try
success = True
Dim cmd As New SqlCommand("DELETE FROM Driver WHERE Driver='" & TextBox1.Text & "';", con)
Catch ex As Exception
MsgBox(ex.Message)
success = False
End Try
If success Then
MsgBox("Success")
End If
con.Close()
check()
You never execute the command. Add a call to SqlCommand::ExecuteNonQuery
Dim cmd As New SqlCommand("DELETE FROM Driver WHERE Driver='" & TextBox1.Text & "';", con)
cmd.ExecuteNonQuery() ' added
That said you should not use string concatenation when adding values to your sql statement. Instead use Parameters which prevents sql injection attacks.
Dim cmd As New SqlCommand("DELETE FROM Driver WHERE Driver= #driver", con)
cmd.Parameters.Add("#driver", SqlDbType.VarChar).Value = TextBox1.Text ' add parameter
cmd.ExecuteNonQuery() ' added
Assumption
Driver is a column inside a table with the same name Driver. If this is not the case then you do not understand tables and columns or the DELETE statement which in its basic form is DELETE FROM [TABLE] WHERE [condition on one or more columns]
When i input the id number of the student in my TextBox, i want the details of that particular students to be displayed in my textbox, without using datagrid. i am using vb.net.
That is my code and i don't understand what's wrong:
cmd.CommandText = "SELECT * from tblsupplier where pro_code = '" & txcode.Text & "'"
dr = cmd.ExecuteReader
txname.Text = dr.item("sup_product")
txprice.Text = dr.Item("sup_price")
First off, you have left your application open to sql injection by adding the value of the textbox inline. Look up this topic please:
http://en.wikipedia.org/wiki/SQL_injection
As for your code, you need to call dr.Read() before you can actually access any properties from your query.
while (dr.Read()) {
// Do stuff
}
Good luck!
It would help if you could provide additional details about the error, friend. Like what SQL you are using, what was the error message, and which event triggers this reader command.
As it stands, it seems you haven't really sent the DataReader to scan the database. You need the While reader.read() loop.
Here is an example, using OLEDB in a function that fetches the data
Note that connstring is my SQL connection string to an Access Database
Dim conn As New System.Data.OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data source=MyDatabase.mdb;Jet OLEDB:Database Password=;")
Function Mycheckifexistss(ByVal sqlCMDstring As String)
Dim sqlCmd As New OLEDBCommand(sqlCMDstring, conn)
Dim reader As OLEDBDataReader
Dim result As String = ""
Try
conn.Open()
reader = sqlcmd.ExecuteReader
While reader.Read
result = reader(0) 'this returns the first field of the queried result.
End While
conn.Close()
Return result
Catch ex As Exception
conn.Close() 'the first thing you want to do in an error, is to close the connection first.
MsgBox(ex.ToString)
End Try
End Function
This is my first attempt at writing a program that accesses a database from scratch, rather than simply modifying my company's existing programs. It's also my first time using VB.Net 2010, as our other programs are written in VB6 and VB.NET 2003. We're using SQL Server 2000 but should be upgrading to 2008 soon, if that's relevant.
I can successfully connect to the database and pull data via query and assign, for instance, the results to a combobox, such as here:
Private Sub PopulateCustomers()
Dim conn As New SqlConnection()
Dim SQLQuery As New SqlCommand
Dim daCustomers As New SqlDataAdapter
Dim dsCustomers As New DataSet
conn = GetConnect()
Try
SQLQuery = conn.CreateCommand
SQLQuery.CommandText = "SELECT Customer_Name, Customer_ID FROM Customer_Information ORDER BY Customer_Name"
daCustomers.SelectCommand = SQLQuery
daCustomers.Fill(dsCustomers, "Customer_Information")
With cboCustomer
.DataSource = dsCustomers.Tables("Customer_Information")
.DisplayMember = "Customer_Name"
.ValueMember = "Customer_ID"
.SelectedIndex = -1
End With
Catch ex As Exception
MsgBox("Error: " & ex.Source & ": " & ex.Message, MsgBoxStyle.OkOnly, "Connection Error !!")
End Try
conn.Close()
End Sub
I also have no problem executing a query that pulls a single field and assigns it to a variable using ExecuteScalar. What I haven't managed to figure out how to do (and can't seem to hit upon the right combination of search terms to find it elsewhere) is how to execute a query that will return a single row and then set various fields within that row to individual variables.
In case it's relevant, here is the GetConnect function referenced in the above code:
Public Function GetConnect()
conn = New SqlConnection("Data Source=<SERVERNAME>;Initial Catalog=<DBNAME>;User Id=" & Username & ";Password=" & Password & ";")
Return conn
End Function
How do I execute a query so as to assign each field of the returned row to individual variables?
You probably want to take a look at the SqlDataReader:
Using con As SqlConnection = GetConnect()
con.Open()
Using cmd As New SqlCommand("Stored Procedure Name", con)
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.Add("#param", SqlDbType.Int)
cmd.Parameters("#param").Value = id
' Use result to build up collection
Using dr As SqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection Or CommandBehavior.SingleResult Or CommandBehavior.SingleRow)
If (dr.Read()) Then
' dr then has indexed columns for each column returned for the row
End If
End Using
End Using
End Using
Like #Roland Shaw, I'd go down the datareader route but an other way.
would be to loop through
dsCustomers.Tables("Customer_Information").Rows
Don't forget to check to see if there are any rows in there.
Google VB.Net and DataRow for more info.