将查询中的数据插入表时,是否会为插入的每条记录运行查询?

时间:2014-02-13 15:12:56

标签: sql vba ms-access access-vba ms-access-2010

我正在将数据插入表中。当我从另一个表中执行此操作时,它很快,只有在有大量记录时才会略微减慢。即使这样,也只需几秒钟。

当我从查询中插入表格时,它会进入分钟 - 每插入1,000条记录大约需要一分钟。

源查询本身,当只是作为选择查询运行时,可能需要1 - 2秒。查询是否针对插入的每条记录运行?我希望它能为整个数据集运行一次。或者,与从另一个表中插入“平面”数据相比,是否有其它因素导致函数运行速度变慢。

我使用的VBA相当无害:

CurrentDb.Execute "SELECT [Extra Value Concatenation].* _
INTO [" & strTableName & "] FROM [Extra Value Concatenation];" 

并且源查询位于下方 - 它使用Allen Browne's Concatenate function

SELECT [Extra Fields - Fee Protection Insurance Concatenate].ContactID,
ConcatRelated('[Fee Protection Insurance]',
'[Extra Fields - Fee Protection Insurance Concatenate]',
'ContactID = ' & [ContactID]) 
AS [Fee Protection Insurance]
FROM [Extra Fields - Fee Protection Insurance Concatenate];

编辑:回答Fionnuala的评论,但我无法在评论中正确格式化。

使用虚构数据,这大致是我想要的。

T1包含客户记录。

ContactID    Name
1            Example Limited
2            Another Company Limited

T2包含额外字段。 ContactID作为外键存在,如果持有多个记录,则可能会重复。

ContactID    FieldValue
1            Value 1
1            Value 2
2            Value 3
2            Value 4
2            Value 5

当我离开加入表格时,T2中的副本显示出来,所以我得到了

ContactID    Name                       FieldValue
1            Example Limited            Value 1
1            Example Limited            Value 2
2            Another Company Limited    Value 3
2            Another Company Limited    Value 4
2            Another Company Limited    Value 5

当我想要的是

ContactID    Name                       FieldValue
1            Example Limited            Value 1; Value 2
2            Another Company Limited    Value 3; Value 4; Value 5

因此,在临时表中连接数据似乎是一个好主意,但却减慢了一切。还有其他方法我应该查看我的查询吗?

3 个答案:

答案 0 :(得分:4)

我已经编写了一个非常基本的模块,与您当前的流程相比,它可以很快完成。请注意,您需要将项目重命名为项目导航窗格中“数据库”以外的其他内容才能使其生效

我假设table1和table2与你上面的相同 table3只是表1中所有记录的列表,其中包含要添加的空白“FieldValues”字段 所需的“value1,value2”等。这将导致Table3填充所需的结果

重要:对于使用记录集.edit和.update函数的任何人,请确保在访问选项菜单中删除记录级别锁定,可以在Access选项的“客户端设置”部分找到它,否则将导致极端在压缩和修复数据库之前,文件膨胀为访问权限不会丢弃记录锁定。这可能会导致数据库在达到Windows的2gb限制时变为不可恢复。

Function addValueField()

'Declarations
Dim db As Database
Dim rs1 As DAO.Recordset
Dim rs2 As DAO.Recordset
Dim qry As String
Dim value As String
Dim recordcount as Long


Set db = CurrentDb()

'Open a select query that is a join of table 1 and table 2
'I have made Contact ID a foreign key in the second table
qry = "SELECT Table1.[Contact ID], Table1.Name, Table2.FieldValue FROM Table1 INNER     JOIN Table2 ON Table1.[Contact ID] = Table2.[Contact ID(FK)] ORDER BY [Contact ID];"

Set rs1 = db.OpenRecordset(qry, dbOpenDynaset)


'Table 3 was filled with each record from table1, with a 3rd "Field Value" field to
'be filled with your Value 1, Value 2 etc.
qry = "SELECT * FROM Table3 ORDER BY [Contact ID]"

Set rs2 = db.OpenRecordset(qry, dbOpenDynaset)

'Ensure you have enough file locks to process records
recordcount = rs1.recordcount
DAO.DBEngine.SetOption DAO.dbMaxLocksPerFile, recordcount + 1000

rs1.MoveFirst
rs2.MoveFirst

'Here we test to see if "Name" is the same in both recordsets, if it is, add the       FieldValue
'to the FieldValue in Table3, otherwise move to the next record in table 3 and compare    again


Do While Not rs1.EOF
    If IsNull(rs2![FieldValue]) = True Then
        If rs2![FieldValue] = "" Then
            rs2.Edit
            rs2![FieldValue] = rs1![FieldValue]
            rs2.Update
            rs1.MoveNext
        Else
            rs2.Edit
            rs2![FieldValue] = rs2![FieldValue] & "; " & rs1![FieldValue]
            rs2.Update
            rs1.MoveNext
        End If
    Else
        rs2.MoveNext
    End If
    Loop
rs1.close
rs2.close
db.close
set db = nothing
set rs1 = nothing
set rs2 = nothing

End Function

答案 1 :(得分:2)

您使用的是用户定义函数(UDF)ConcatRelated,因此UDF会针对每条记录运行,否则,通常Access SQL会以正常方式运行。

答案 2 :(得分:1)

以pegicity的答案为基础,我最终的代码是:

Option Compare Database

Sub Concatenate(strTableToConcatenate As String, strFieldToConcatenate As String, strIDField As String)

Dim rsSource As DAO.Recordset
Dim rsDestination As DAO.Recordset
Dim qry As String
Dim strSourceTable As String
Dim i As Integer
Dim strFieldName As String
Dim strValue As String
Dim intConcatenateID As Integer
Dim intSortID As Integer

strSourceTable = strTableToConcatenate & " (Concatenate)" 'Creates a duplicate copy of the table to be concatenated and empties the original table'
DeleteTable (strSourceTable)
DoCmd.CopyObject , strSourceTable, acTable, strTableToConcatenate
qry = "DELETE FROM [" & strTableToConcatenate & "]"
CurrentDb.Execute (qry)

qry = "ALTER TABLE [" & strTableToConcatenate & "] ALTER COLUMN [" & strFieldToConcatenate & "] memo" 'Changes the DataType of the field to be concatenated to Memo, as the result may be considerably longer than the original data'
CurrentDb.Execute (qry)

i = 0
intCurrentID = 0

qry = "SELECT * FROM [" & strSourceTable & "] ORDER BY [" & strIDField & "], [" & strFieldToConcatenate & "]"
Set rsSource = CurrentDb.OpenRecordset(qry, dbOpenDynaset)
qry = "SELECT * FROM [" & strTableToConcatenate & "]"
Set rsDestination = CurrentDb.OpenRecordset(qry, dbOpenDynaset)

For Each fld In rsSource.Fields 'Finds the column number of the fields you are sorting by and concatenating from your source table.'
    strFieldName = rsSource.Fields(i).Name
    If strFieldName = strFieldToConcatenate Then
        intConcatenateID = i
    ElseIf strFieldName = strIDField Then
        intSortID = i
    End If
    i = i + 1
Next

If rsSource.recordcount <> 0 Then

    rsSource.MoveFirst
    intCurrentID = rsSource.Fields(intSortID).Value
    strConcatenateValue = ""

    While Not rsSource.EOF 'The source recordset is sorted by your designated sort field, so any duplicates of that field will be next to each other. If the row below has the same id as the row above, the sub continues to build the concatenated value. If the row changes, it adds the concatenated value to the destination record set.'

       If intCurrentID = rsSource.Fields(intSortID).Value Then

            strConcatenateValue = strConcatenateValue & "," & rsSource.Fields(intConcatenateID).Value
            rsSource.MoveNext

       Else
            rsDestination.AddNew

            i = 0

            If Len(strConcatenateValue) > 0 Then
                strConcatenateValue = Right(strConcatenateValue, Len(strConcatenateValue) - 1)
            End If

            For Each fld In rsSource.Fields
                strFieldName = rsSource.Fields(i).Name
                If strFieldName = strFieldToConcatenate Then
                     strValue = strConcatenateValue
                ElseIf strFieldName = strIDField Then
                    strValue = intCurrentID
                Else
                    strValue = rsSource.Fields(i).Value
                End If
                rsDestination.Fields(strFieldName) = "" & strValue & ""
                i = i + 1
            Next

            rsDestination.Update
            intCurrentID = rsSource.Fields(intSortID).Value
            strConcatenateValue = ""

       End If

     Wend

End If

rsSource.Close
rsDestination.Close
Set rsSource = Nothing
Set rsDestination = Nothing

End Sub