Ms Access - System resource exceeded inserting row

2019-01-12 12:19发布

问题:

I want to insert 1500 rows to Ms access database from vb.net datagridview.

Inserting up to 400 rows no issue, but above 400 rows its showing error - System resource exceeded.

Im using below code. The error is highlighting to:

readinputs = dbup.ExecuteReader() and sometimes
.ExecuteNonQuery()

Dim Dbcon As New OleDbConnection(connStr)

Dbcon.Open()

Dim query As String
Dim dbup As New OleDbCommand
Dim readinputs As OleDbDataReader

For x As Integer = 0 To IncomingMailDGV.Rows.Count - 1
    Dim received As String = IncomingMailDGV.Rows(x).Cells(0).Value
    Dim subject As String = IncomingMailDGV.Rows(x).Cells(1).Value
    Dim contents As String = IncomingMailDGV.Rows(x).Cells(2).Value

    query = "SELECT ReceivedDateTime, Subject, MessageContents FROM IncomingAlerts WHERE ReceivedDateTime = @ReceivedDateTime AND MessageContents =@MessageContents"
    dbup = New OleDbCommand(query, Dbcon)
    dbup.Parameters.AddWithValue("ReceivedDateTime", received)
    dbup.Parameters.AddWithValue("MessageContents", contents)
    readinputs = dbup.ExecuteReader()

    If readinputs.HasRows = False Then

        Dim InsertData As String
        InsertData = "INSERT INTO IncomingAlerts(ReceivedDateTime, Subject, MessageContents) Values (@ReceivedDateTime, @Subject, @MessageContents)"
        dbup = New OleDbCommand(InsertData)
        dbup.Parameters.AddWithValue("ReceivedDateTime", received)
        dbup.Parameters.AddWithValue("Subject", subject)
        dbup.Parameters.AddWithValue("MessageContents", contents)

        With dbup
            .CommandText = InsertData
            .Connection = Dbcon
            .ExecuteNonQuery()
        End With

    End If

Next

回答1:

Because of the loop, you are creating up to 2 OleDbCommand objects per row (one for the SELECT and maybe one for the UPDATE), but never disposing of them. You could use cmd.Parameters.Clear to reuse them, but I would chop that thing up into a control procedure to make it simpler. Something like this:

' if AllowUsersToAddRows is true, this will loop one too many:
For x As Integer = 0 To IncomingMailDGV.Rows.Count - 1
    Dim received = IncomingMailDGV.Rows(x).Cells(0).Value.ToString
    Dim contents  = IncomingMailDGV.Rows(x).Cells(2).Value.ToString
    Dim subject  = IncomingMailDGV.Rows(x).Cells(1).Value.ToString

    If ItemExists(received, contents) = False Then
        InsertItem(received, contents, subject)
    End If
Next

Then helpers which are self contained and clean up after themselves:

Private Function ItemExists(received As String, 
       contents As String) As Boolean
    Dim query As String = "SELECT ReceivedDateTime, Subject, MessageContents FROM IncomingAlerts WHERE ReceivedDateTime = @ReceivedDateTime AND MessageContents =@MessageContents"
    Using dbcon As New OleDbConnection(connstr)
        dbcon.Open
        Using cmd As New OleDbCommand(query, dbcon)
            cmd.Parameters.AddWithValue(("ReceivedDateTime", received)
            cmd.Parameters.AddWithValue("MessageContents", contents)

            ' Better to convert the query to a SELECT COUNT
            ' cmd.ExecuteScalar would not require a Reader
            Using rdr = cmd.ExecuteReader
                Return rdr.HasRows
            End Using
        End Using    
    End Using

End Function

Private Function InsertItem(received As String, 
                 contents As String, subj As String) As Boolean
    Dim sql = "INSERT INTO IncomingAlerts(ReceivedDateTime, Subject, MessageContents) Values (@ReceivedDateTime, @Subject, @MessageContents)"

    Dim rows As Integer
    Using dbcon As New OleDbConnection(connstr)
        Using cmd As New OleDbCommand(sql, dbcon)
            dbcon.Open
            cmd.Parameters.AddWithValue("@ReceivedDateTime", received)
            cmd.Parameters.AddWithValue("@Subject", subj)
            cmd.Parameters.AddWithValue("@MessageContents", contents)
            rows = cmd.ExecuteNonQuery
            Return rows <> 0
        End Using
    End Using
End Function

I also made them a little shorter by using the constructor overloads. For instance, with OleDbCommand, I pass the SQL and the connection to it when it is created rather than set those properties individually.

As is, it gets done only once. There are other things you could do such as just use SQL Count to determine if the are any matching rows etc. Using a DataTable and FindRow would also prevent having to hot the DB to see if something exists.

The main point is disposing of Connection, Command and DataReader objects when you are done with them.