The database is locked error appears even after I have disposed all the commands. I'm trying to write to the database and it is failing on the INSERT command at the bottom. I had it working before but for some reason it has started to fail now.
Sub Btn_SubmitClick(sender As Object, e As EventArgs)
If MsgBox("Are you sure?",vbYesNo,"Submit?") = 7 Then
'returns to previous screen
Else
'commences insert into database
Rows = (dataGridView1.RowCount - 1)
While count < rows
'putting grid stuff into variables
DateStart = dataGridView1.Rows(Count).Cells(0).Value.ToString
DateEnd = dataGridView1.Rows(Count).Cells(2).Value.ToString 'note other way round
TimeStart = dataGridView1.Rows(Count).Cells(1).Value.ToString
TimeEnd = dataGridView1.Rows(Count).Cells(3).Value.ToString
TotalHours = dataGridView1.Rows(Count).Cells(4).Value.ToString
OccuranceNo = dataGridView1.Rows(Count).Cells(5).Value.ToString
'fetching reason ID for Storage
SQLcommand = SQLconnect.CreateCommand
SQLcommand.CommandText = "SELECT Reason_ID FROM Reasons WHERE Reason_Name = '" & dataGridView1.Rows(Count).Cells(6).Value.ToString & "'"
SQLreader = SQLcommand.ExecuteReader
ReasonID = SQLreader("Reason_ID")
SQLcommand.Dispose
'fetching site ID for storage
SQLcommand = SQLconnect.CreateCommand
SQLcommand.CommandText = "SELECT Site_ID FROM Sites WHERE Site_Name = '" & dataGridView1.Rows(Count).Cells(7).Value.ToString & "'"
SQLreader = SQLcommand.ExecuteReader
SiteID = SQLreader("Site_ID")
SQLcommand.Dispose
Oncall = dataGridView1.Rows(Count).Cells(8).Value.ToString
'increment counter
Count = Count + 1
'send to database
SQLcommand = SQLconnect.CreateCommand
SQLcommand.CommandText = "INSERT INTO Shifts (Staff_ID, Date_Shift_Start, Date_Shift_End, Time_Shift_Start, Time_Shift_End, Total_Hours, Occurance_No, Site_ID, On_Call_Req, Rate, Approved, Reason_ID) VALUES ('" & userID & "' , '" & DateStart &"' , '" & DateEnd & "' , '" & TimeStart & "' , '" & TimeEnd & "' , '" & TotalHours & "' , '" & OccuranceNo & "' , '" & SiteID & "' , '" & Oncall & "' , '"& "1" & "' , '" & "N" & "' , '" & ReasonID & "')"
SQLcommand.ExecuteNonQuery()
SQLcommand.Dispose
End While
MsgBox("Ok")
End If
End Sub
There are several things which ought be changed in the code shown. Since none of the Connection, Command or Reader objects are declared in the code, they must be global objects you are reusing. Don't do that.
There can be reasons for one persistent connection, but queries are very specific in nature, so trying to reuse DbCommand and DataReaders can be counterproductive. Since these work closely with the DbConnection, all sorts of bad things can happen. And it means the root of the problem could be anywhere in your code.
The following will loop thru a DGV to insert however many rows there are.
Dim SQL = "INSERT INTO Sample (Fish, Bird, Color, Value, Price) VALUES (#f, #b, #c, #v, #p)"
Using dbcon As New SQLiteConnection(LiteConnStr)
Using cmd As New SQLiteCommand(SQL, dbcon)
dbcon.Open()
cmd.Parameters.Add("#f", DbType.String)
cmd.Parameters.Add("#b", DbType.String)
cmd.Parameters.Add("#c", DbType.String)
cmd.Parameters.Add("#v", DbType.Int32)
cmd.Parameters.Add("#p", DbType.Double)
Dim fishName As String
For Each dgvR As DataGridViewRow In dgv2.Rows
' skip the NewRow, it has no data
If dgvR.IsNewRow Then Continue For
' look up from another table
' just to shorten the code
userText = dgvR.Cells(0).Value.ToString()
fishName = dtFish.AsEnumerable().
FirstOrDefault(Function(f) f.Field(Of String)("Code") = userText).
Field(Of String)("Fish")
' or
'Dim drs = dtFish.Select(String.Format("Code = '{0}'", userText))
'fishName = drs(0)("Fish").ToString()
cmd.Parameters("#f").Value = fishName
cmd.Parameters("#b").Value = dgvR.Cells(1).Value
cmd.Parameters("#c").Value = dgvR.Cells(2).Value
cmd.Parameters("#v").Value = dgvR.Cells(3).Value
cmd.Parameters("#p").Value = dgvR.Cells(4).Value
cmd.ExecuteNonQuery()
Next
End Using
End Using
NOTE: Like the original code there is no Data Validation - that is, it assumes that whatever they typed is always valid. This is rarely a good assumption.
The code implements Using blocks which will declare and create the target objects (dbCommands, connections) and dispose of them when done with them. They cannot interfere with code elsewhere because they only exist in that block.
SQL Parameters are used to simplify code and specify datatypes. A side effect of concatenating SQL as you are, is that everything is passed as string! This can be very bad with SQLite which is typeless.
I would avoid firing off multiple look up queries in the loop. The original code should throw an InvalidOperationException since it never Reads from the DataReader.
Perhaps the best way to do this would be for Sites and Reasons to be ComboBox column in the DGV where the user sees whatever text, and the ValueMember would already be available for the code to store.
Another alternative shown in the answer is to pre-load a DataTable with the data and then use some extension methods to look up the value needed.
If you "must" use readers, implement them in their own Using blocks.
A For Each loop is used which provides the actual row being examined.
I'd seriously reconsider the idea of storing something like a startDateTime as individual Date and Time fields.
These are the basics if using DB Provider objects. It is not certain that refactoring that procedure shown will fix anything, because the problem could very well be anywhere in the code as a result of DBProvider objects left open and not disposed.
One other thing to check is any UI Manager you may be using for the db. Many of these accumulate changes until you click a Save or Write button. In the interim, some have the DB locked.
Finally, even though the code here is shorter and simpler, using a DataAdapter and a DataTable would allow new data in the DGV to automatically update the database:
rowsAffected = myDA.Update(myDT)
It would take perhaps 30 mins to learn how to configure it that way.
Related
I am trying to save multiple data to my database with this code:
repNo = MainForm.StaffMixname.Text.Substring(0, 3) & DateTime.Now.ToString("yyyyMMddhhmmss")
MetroGrid5.DataSource = Nothing
Dim ds As DataSet = New DataSet
Dim Query As String = "SELECT ci.seq_id, CONCAT(ci.lastname, ci.firstname) AS fullname, ci.amountApplied, ci.province, co.kind, co.specifications, co.regOwner, co.location FROM clientinformation ci LEFT JOIN collateraloffered co ON ci.seq_id=co.seq_id WHERE co.kind IS NOT NULL AND ci.province = '" & MetroComboBox8.Text & "' AND ci.seq_id BETWEEN '" & convertedstrFrom.ToString("yyMMdd") & "%' AND '" & convertedstrTo.ToString("yyMMdd") & "%'"
Dim fetch As New MySqlDataAdapter(Query, connect)
fetch.Fill(ds, "collateral")
MetroGrid5.DataSource = ds.Tables("collateral")
If MetroGrid5.Rows.Count > 0 Then
Dim cm As New MySqlCommand
With cm
.Connection = connect
For i As Integer = 0 To MetroGrid5.RowCount - 1
.CommandText = _
"INSERT INTO collateralrpt Values('" & repNo & _
"', '" & MetroGrid5.Rows(i).Cells("seq_id").Value & _
"', '" & MetroGrid5.Rows(i).Cells("fullname").Value & _
"', '" & MetroGrid5.Rows(i).Cells("amountApplied").Value & _
"', '" & MetroGrid5.Rows(i).Cells("kind").Value & _
"', '" & MetroGrid5.Rows(i).Cells("specifications").Value & _
"', '" & MetroGrid5.Rows(i).Cells("regOwner").Value & _
"', '" & MetroGrid5.Rows(i).Cells("location").Value & _
"', '" & MetroGrid5.Rows(i).Cells("province").Value & "')"
.ExecuteNonQuery()
Next
End With
cm.Dispose()
cm = Nothing
With connect
.Close()
.Dispose()
End With
Else
MsgBox("No Data!")
End If
but unfortunately It shows MySqlException Column count doesn't match value count at row 1.
Is there any mistake with the code above? thanks in advance.
If you want to retrieve data from one table(s) and insert into another, just use a single data adapter. You can even do so if the tables are in a different database - you just need a different connection for the SelectCommand and InsertCommand. Only a single connection is required for a single database though. E.g.
Dim selectSql = "SELECT Column1A, Column1B FROM Table1"
Dim insertSql = "INSERT INTO Table2
(
Column2A,
Column2B
)
VALUES
(
#Column2A,
#Column2B
)"
Using connection As New MySqlConnection("connection string here"),
insertCommand As New MySqlCommand(insertSql, connection),
adapter As New MySqlDataAdapter(selectSql, connection) With {.InsertCommand = insertCommand, .AcceptChangesDuringFill = False}
With insertCommand.Parameters
.Add("#Column2A", MySqlDbType.Int, 0, "Column1A")
.Add("#Column2B", MySqlDbType.VarChar, 50, "Column1B")
End With
Dim table As New DataTable
connection.Open()
adapter.Fill(table)
adapter.Update(table)
End Using
There are a few things to note here.
I wrote the SQL code using a single, multiline literal. That is far
more readable that concatenating every line.
I used parameters. That prevents numerous issues that have been
written about ad nauseum so I won't go into it here.
There's no grid control involved here. You can add one and bind the DataTable after calling Fill but that is only so you can see the data. It has nothing to do with the actual code of retrieving and saving.
The connection is opened explicitly. You can normally let a Fill or Update call open and close the connection implicitly but, in this case, we want to perform both operations and closing and reopening the connection in between is an unnecessary overhead.
The disposable objects are created with a Using statement, which means they will be implicitly disposed at the end of the block. That inclides closing the connection.
The AcceptChangesDuringFill property of the data adapter is set to True so that all RowStates are left as Added so that all DataRows are ready to be inserted.
So here is the predefined SQL statement that is stored in the DAO file. The values are coming from a class. The picture value is an image converted to a byte(). This class is written in VB.net. I'm in a new job and in my previous i used angular and the entity framework so writing SQL statements is new to me. I'm trying to follow existing examples from co workers but they have never inserted images into the database before so i'm kinda on my own. Yes i know i could just store the files in the server and save the paths to them in the database but for whatever reason my network team wants it stored in the database as blobs. So, here is the SQL statement.
"INSERT INTO AuthAccessID" &
"(" &
"FName," &
"MName," &
"LName," &
"Suffix," &
"Address," &
"AddressExt," &
"City," &
"State," &
"Zip," &
"LawFirm," &
"Picture," &
"AddedDate," &
"AddedBy," &
")" &
"VALUES(" &
"" & ReplaceApostrophes(pp.FName) & ", " &
"'" & ReplaceApostrophes(pp.MName) & "', " &
"'" & ReplaceApostrophes(pp.LName) & "', " &
"'" & ReplaceApostrophes(pp.Suffix) & "', " &
"'" & ReplaceApostrophes(pp.Address) & "', " &
"'" & ReplaceApostrophes(pp.AddressExt) & "', " &
"'" & ReplaceApostrophes(pp.City) & "', " &
"'" & ReplaceApostrophes(pp.State) & "', " &
"'" & ReplaceApostrophes(pp.Zip) & "', " &
"'" & ReplaceApostrophes(pp.LawFirm) & "', " &
"'" & pp.Picture & "', " &
"'" & pp.AddedDate & "', " &
"'" & ReplaceApostrophes(pp.AddedBy) & "')
the pp.Picture is the Byte(). The error i'm getting is:
Operator '&' is not defined for types 'String' and 'Byte()'
i have googled around but cannot find anything. Does anyone have any idea how to correct this? or is there a better way to write the SQL statement? If i can't get this to work the network team said i can use the server file method but they are really pushing the blob in SQL storage instead. Thanks in advance.
Always use Parameters to avoid sql injection, make you sql statement easier to write and read, and make sure you are sending the correct datatypes. Parameters will also allow apostrophes. Use the .Add method. See http://www.dbdelta.com/addwithvalue-is-evil/
and
https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/
and another one:
https://dba.stackexchange.com/questions/195937/addwithvalue-performance-and-plan-cache-implications
Here is another
https://andrevdm.blogspot.com/2010/12/parameterised-queriesdont-use.html
In the code below, I had to guess at the SqlDbType and Size. Check your database for the correct information.
Connections and commands are using unmanaged resources. They release these resources in their .Dispose method so this method must be called. Using...End Using blocks take care of closing and disposing objects even if there is an error.
I assumed pp was an instance of a class. I gave the class the name Person. Correct this to the real class name.
Private ConStr As String = "Your connection string"
Private Sub InsertAuthAccessID(pp As Person)
Dim sql = "INSERT INTO AuthAccessID (
FName,
MName,
LName,
Suffix,
Address,
AddressExt,
City,
State,
Zip,
LawFirm,
Picture,
AddedDate,
AddedBy)
VALUES (
#FName,
#MName,
#LName,
#Suffix,
#Address,
#AddressExt,
#City,
#State,
#Zip,
#LawFirm,
#Picture,
#AddedDate,
#AddedBy)"
Using cn As New SqlConnection(ConStr),
cmd As New SqlCommand(sql, cn)
cmd.Parameters.Add("#FName", SqlDbType.VarChar, 50).Value = pp.FName
cmd.Parameters.Add("#MName", SqlDbType.VarChar, 50).Value = pp.MName
cmd.Parameters.Add("#LName", SqlDbType.VarChar, 100).Value = pp.LName
cmd.Parameters.Add("#Suffix", SqlDbType.VarChar, 20).Value = pp.Suffix
cmd.Parameters.Add("#Address", SqlDbType.VarChar, 200).Value = pp.Address
cmd.Parameters.Add("#AddressExt", SqlDbType.VarChar, 50).Value = pp.AddressExt
cmd.Parameters.Add("#City", SqlDbType.VarChar, 100).Value = pp.City
cmd.Parameters.Add("#State", SqlDbType.VarChar, 50).Value = pp.State
cmd.Parameters.Add("#Zip", SqlDbType.VarChar, 20).Value = pp.Zip
cmd.Parameters.Add("#LawFirm", SqlDbType.VarChar, 200).Value = pp.LawFirm
cmd.Parameters.Add("#Picture", SqlDbType.VarBinary).Value = pp.Picture
cmd.Parameters.Add("#AddedDate", SqlDbType.Date).Value = pp.AddedDate
cmd.Parameters.Add("#AddedBy", SqlDbType.VarChar, 50).Value = pp.AddedBy
cn.Open()
cmd.ExecuteNonQuery()
End Using
End Sub
EDIT:
In older versions of VB that did not support multiline String literals, you can use an XML literal instead:
Dim sql = <sql>
INSERT INTO AuthAccessID (
FName,
MName,
LName,
Suffix,
Address,
AddressExt,
City,
State,
Zip,
LawFirm,
Picture,
AddedDate,
AddedBy)
VALUES (
#FName,
#MName,
#LName,
#Suffix,
#Address,
#AddressExt,
#City,
#State,
#Zip,
#LawFirm,
#Picture,
#AddedDate,
#AddedBy)
</sql>
Using cn As New SqlConnection(ConStr),
cmd As New SqlCommand(sql.Value, cn)
Too long and involved for a comment. You have the following snippet in your code:
")" &
"VALUES(" &
"" & ReplaceApostrophes(pp.FName) & ", " &
"'" & ReplaceApostrophes(pp.MName) & "', " &
That is an error. FName is a string and must be treated in exactly the same manner as you do with MName. It is missing the single quote delimiters.
More generally, this approach relies on converting all your "fields" into literals to embed them as strings within your tsql statement. So the question now becomes how do you "write" a binary literal in tsql. You would do that by generating a string like this: 0x69048AEFDD010E. Documentation for tsql constants is here. Knowing that, the next issue is how to do that in your dev language - which is not something I can answer. This look promising.
But before you go down this path, use parameterization and you NEVER have to deal with this ever again.
I come from a MSAccess background, so I code quite much the same way I did in VBA or now with VB.net
Here the code I would use:
Dim sFields() As String
sFields = Split("FName,MName,LName,Suffix,Address,AddressExt,City,State,Zip,LawFirm,AddedDate,AddedBy", ",")
Dim rst As DataTable
Dim da As SqlDataAdapter
rst = MyrstEdit("select * from AuthAccessID where id = 0", da, strcon)
With rst.Rows.Add
For Each s In sFields
.Item(s) = GetValue(pp, s)
Next
End With
da.Update(rst)
And I have two helper routines. The first one gets any class property by a "string" value.
Since by luck, you have field names and the class members are the same!
Public Function GetValue(ByRef parent As Object, ByVal fieldName As String) As Object
Dim field As FieldInfo = parent.[GetType]().GetField(fieldName, BindingFlags.[Public] Or BindingFlags.Instance)
Return field.GetValue(parent)
End Function
And then I have a datable routine - that gets me the data table, and is this:
Public Function MyrstEdit(strSQL As String, ByRef oReader As SqlDataAdapter) As DataTable
Dim mycon As New SqlConnection(strCon)
oReader = New SqlDataAdapter(strSQL, mycon)
Dim rstData As New DataTable
Dim cmdBuilder = New SqlCommandBuilder(oReader)
Try
oReader.Fill(rstData)
oReader.AcceptChangesDuringUpdate = True
Catch
End Try
Return rstData
End Function
So, to get all the data types and structure? I pass a dummy sql that returns no rows. (no rows are returned, but we DO GET the valuable table data types when we do this dummy table pull!). In most cases, if the PK is a autonumber, then I use id = 0.
that same MyRstEdit() code bit has tons of uses! You can now deal with a table in a nice structure, loop it, shove it into a combo box, or datagrid. And as it shows, also allows editing of the data - all with type checking.
The REAL trick and tip I am sharing here? Break out your common data routines to about 2-3 routines like MyRstEdit().
That way, you really don't have to deal with messy in-line sql, or every time you need to work on a table, you don't wire truckloads of code. And the real beauty here is that data typing is done for you - you don't have line after line of parameters, nor line after line of data typing for each column.
So, I hope this post gives you some ideas. But it also nice since I get to code much like I did in MSAccess, and that includes writing VERY little code for updates such as this.
The ideas here are just that - a different approach. The other approaches here are also just fine. (but are quite a bit more code then I perfer).
There are times when using a data table is a rather nice - and I think this is such an example.
And while I am oh so often used to referencing columns as a table collection? The cool trick here is I am also referencing each member of the class with a string too!
In my program I have a function titled runSQL, here it is:
Dim Connection As New OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=TrainingLog.accdb")
Dim DT As New DataTable
Dim DataAdapter As OleDb.OleDbDataAdapter
Connection.Open()
DataAdapter = New OleDb.OleDbDataAdapter(query, Connection)
DT.Clear()
DataAdapter.Fill(DT)
Connection.Close()
Return DT
And I'm trying to update a record in a database using the update string, sourced from this code:
Dim sqlString As String
sqlString = "UPDATE UserRecords set FirstName = '" & txtName.Text
sqlString = sqlString & "', LastName = '" & txtSurname.Text
If ChkSmoker.Checked = True Then
sqlString = sqlString & "', Smoker = true"
ElseIf ChkSmoker.Checked = False Then
sqlString = sqlString & "', Smoker = false"
End If
sqlString = sqlString & ", Weight = " & txtWeight.Text
If RdoMale.Checked = True Then
sqlString = sqlString & ", Gender = 'm'"
ElseIf RdoFemale.Checked = True Then
sqlString = sqlString & ", Gender = 'f'"
End If
sqlString = sqlString & " WHERE UserName = '" & LstUsers.SelectedItem.ToString & "'"
runSQL(sqlString)
However once I click the save button, I get an error from line 7 of the runSQL function (not including empty line, so that's the DataAdapter.Fill(DT) line) which says "No value given for one or more required parameters."
I wondered if anyone knew why this is or how to fix it.
One thing I did think of is that, in the table being updated, there are fields other than those being mentioned in my UPDATE statement. For example there is a Yes/no field titled "TeamMember", which I don't mention in the update statement.
When using the update function, do I have to give values for every field, even those not being changed?
Thanks for reading, and hopefully helping!
You should never composea SQL query yourself. It much easies and safer (to vaoid SQL injection) to create a parameterized query, or use an stored procedure. And then execute it by pasing the query or stored procedure name and the parameter values.
Besides, in this way, you don't have to take care of what the right format is for a particular value. For example, how do you format a date? And, how do you format a boolean value? Most probably the problem with your query is the false or true value that you're trying to set for the Smoker column, because in TSQL that's a bit value, and can only be 0 or 1.
Check this to see samples of using parameters: ADO.NET Code Examples (Click the VB tab to see it in VB). You'll see that you define a parameter specifying a name with an # prefix in the query, and then you simply pass a value for each parameter in the query, and it will be passed to the server in the correct format without you taking care of it.
Taken from one of the samples:
Dim queryString As String = _
"SELECT ProductID, UnitPrice, ProductName from dbo.Products " _
& "WHERE UnitPrice > #pricePoint " _
& "ORDER BY UnitPrice DESC;"
Dim command As New SqlCommand(queryString, connection)
command.Parameters.AddWithValue("#pricePoint", paramValue)
'' command.ExecuteXXX
NOTE that you can execute the command in different ways, depending on your need to simply execute it or get an scalar value or a full dataset as a result.
can someone help me with my code, i need to check first if record exist. Well i actually passed that one, but when it comes to inserting new record. im getting the error "There is already an open DataReader associated with this Command which must be closed first." can some help me with this? thanks.
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim reg_con As SqlConnection
Dim reg_cmd, chk_cmd As SqlCommand
Dim checker As SqlDataReader
Dim ID As Integer
Dim fname_, mname_, lname_, gender_, emailadd_, college_, password_ As String
ID = idnumber.Value
fname_ = fname.Value.ToString
mname_ = mname.Value.ToString
lname_ = lname.Value.ToString
gender_ = gender.Value.ToString
college_ = college.Value.ToString
emailadd_ = emailadd.Value.ToString
password_ = reg_password.Value.ToString
reg_con = New SqlConnection("Data Source=JOSH_FLYHEIGHT;Initial Catalog=QceandCceEvaluationSystemDatabase;Integrated Security=True")
reg_con.Open()
chk_cmd = New SqlCommand("SELECT IDnumber FROM UsersInfo WHERE IDnumber = '" & ID & "'", reg_con)
checker = chk_cmd.ExecuteReader(CommandBehavior.CloseConnection)
If checker.HasRows Then
MsgBox("Useralreadyexist")
Else
reg_cmd = New SqlCommand("INSERT INTO UsersInfo([IDnumber], [Fname], [Mname], [Lname], [Gender], [Emailadd], [College], [Password]) VALUES ('" & ID & "', '" & fname_ & "', '" & mname_ & "', '" & lname_ & "', '" & gender_ & "', '" & emailadd_ & "', '" & college_ & "', '" & password_ & "')", reg_con)
reg_cmd.ExecuteNonQuery()
End If
reg_con.Close()
End Sub
Add this string to your connection string
...MultipleActiveResultSets=True;";
Starting from Sql Server version 2005, this string allows an application to maintain multiple active statements on a single connection. Without it, until you close the SqlDataReader you cannot emit another command on the same connection used by the reader.
Apart from that, you insert statement is very dangerous because you use string concatenation. This is a well known code weakness that could result in an easy Sql Injection vulnerability
You should use a parameterized query (both for the insert and for the record check)
reg_cmd = New SqlCommand("INSERT INTO UsersInfo([IDnumber], ......) VALUES (" & _
"#id, ......)", reg_con)
reg_cmd.Parameters.AddWithValue("#id", ID)
.... add the other parameters required by the other field to insert.....
reg_cmd.ExecuteNonQuery()
In a parameterized query, you don't attach the user input to your sql command. Instead you put placeholders where the value should be placed (#id), then, before executing the query, you add, one by one, the parameters with the same name of the placeholder and its corresponding value.
You need to close your reader using checker.Close() as soon as you're done using it.
Quick and dirty solution - issue checker.Close() as a first command of both IF and ELSE block.
But (better) you don't need a full blown data reader to check for record existence. Instead you can do something like this:
chk_cmd = New SqlCommand("SELECT TOP (1) 1 FROM UsersInfo WHERE IDnumber = '" & ID & "'", reg_con)
Dim iExist as Integer = chk_cmd.ExecuteScalar()
If iExist = 1 Then
....
This approach uses ExecuteScalar method that returns a single value and doesn't tie the connection.
Side note: Instead of adding parameters like you do now - directly to the SQL String, a much better (and safer) approach is to use parametrized queries. Using this approach can save you a lot of pain in the future.
what i got:
two mdb databases and one application to insert information (rows) from db1 to db2.
when i'am runing my code there is an exception:
System resource exceeded.
the code:
Connection Strings:
Dim db2Connection As New OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\db2.mdb;Persist Security Info=False;")
Dim db1Connection As New OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=c:\db1.mdb;Persist Security Info=False;")
Code to copy the information:
Dim DataAddapter As New OleDb.OleDbDataAdapter
Dim ds As New DataSet
'Open DB1 Connection:
db1Connection.open()
'Select All From M
DataAddapter.SelectCommand = New OleDb.OleDbCommand("SELECT * FROM M", db1Connection)
Dim cmd As OleDb.OleDbCommand = DataAddapter.SelectCommand
Dim Reader As OleDb.OleDbDataReader = cmd.ExecuteReader()
'Before Reading Results From DB1 Lets Open DB2Connection:
db2Connection.open()
'Start Reading Results in LOOP:
Do Until Reader.Read() = False
Dim F_Name As String = Reader("F_NAME")
Dim L_Name As String = Reader("L_NAME")
Dim CITY As String = Reader("NAME_CITY")
F_Name = Replace(F_Name, "'", "")
L_Name = Replace(L_Name, "'", "")
'Start Moving The Results To Db2(Insert):
'--------------------------------------
Dim Exist As Integer = 0
Dim c As New OleDb.OleDbCommand
c.Connection = db2Connection
c.CommandText = "SELECT COUNT(*) FROM `Names` WHERE `LastName`='" & L_Name & "' AND `FirstName`='" & F_Name & "' AND `City`='" & CITY & "'"
'----------------------------------------
'Exception Here!! :(
'This Line Checking If Already Exist
Exist = CLng(c.ExecuteScalar())
'----------------------------------------
If Exist = 0 Then
c.CommandText = "INSERT INTO `Names` (`LastName`,`FirstName`,`City`) VALUES ('" & L_Name & "','" & F_Name & "','" & CITY & "')"
c.ExecuteNonQuery()
'Note: After this line i'am getting the Exception there... (2 queries executed ExecuteScalar + ExecuteNonQuery) maybe i need to create connection for every query? :S
End If
Loop
another thing:
i have to send the query to db2 in this syntax(Otherwise it does not work):
INSERT INTO `Names` (`LastName`,`FirstName`,`City`) VALUES ('" & L_Name & "','" & F_Name & "','" & CITY & "')
i have to use the -> ` <- to the name of the columns,
but when i'am sending a query to db1 without -> ` <- it's working. :S and i dont know what is the difference between db1 to db2 but its very strange maybe my problem is there...
good answer is a good example plus good explanation :).(c# or vb.net)
You are prime for sql-injection... You should read-up on that, and at a minimum, PARAMETERIZE your sql commands, do NOT build string statement to execute with embedded values. I don't specifically know how db2 handles parameters... some use "?" as place-holders, SQL-Server uses "#" and Advantage Database uses ":".. but in either case, here is the principle of it...
c.CommandText = "select blah from `names` where LastName = ? and FirstName = ? and City = ?"
c.CommandText = "select blah from `names` where LastName = #parmLastName and FirstName = #parmFirstName and City = #parmCity"
For the named parameters above (such as #parmLastName), I am prefixing with "parm" for sole purpose of differentiating a value vs the actual COLUMN name
Then, your parameters would be something like
c.Parameters.Add( "#parmLastName", yourLastNameVariable )
c.Parameters.Add( "#parmFirstName", yourFirstNameVariable)
c.Parameters.Add( "#parmCity", yourCityVariable)
If using the "?" version of parameters where they are not explicitly named, you need to just make sure your parameter context is in the same sequence as the "?" place-holders.
Then execute your call... Same principle would apply for all your queries (select, insert, update, delete)
As for your system resources... how many records are you pulling down. It could just be choking your system memory resources trying to pull down the entire database table. You might want to break down based on one alphabetic letter at a time...
Also, a link from MS about system resources and Access via a patch.