如何编写 SQL 命令将所有旧数据从访问表移动到另一个数据库/表?
Posted
技术标签:
【中文标题】如何编写 SQL 命令将所有旧数据从访问表移动到另一个数据库/表?【英文标题】:How to write a SQL command to move all old data from access table into another database/table? 【发布时间】:2014-11-16 11:02:21 【问题描述】:我在 MS Access 2003 中有一个表,我想在其中存档所有旧数据。
标准是创建数据应小于特定日期。
我可以编写一条 SQL 语句来选择它们,但我不知道如何将它们移动到另一个数据库/表?假设存档数据库/表已经创建并且数据结构与当前表匹配。
还有我如何确保所有移到存档表的数据都从当前表中删除?
我想编写 VBA 代码来运行命令检查数据是否正确存档。
【问题讨论】:
re:第 (1) 部分 - 请参阅下面 Jeeped 的回答。回复:第 (2) 部分 - 这对于 Stack Overflow 问题来说太宽泛了。接受下面的(其中一个)答案,然后忙起来写一些 VBA 代码。如果您需要帮助,请先查看How to Ask,然后再查看ask a new question。 【参考方案1】:您希望 1) 将满足特定条件的数据从一个表格移动到另一个具有相同格式的现有表格。 2)您要“确保从当前表中删除移动到存档表的所有数据”。 3)您“想要编写 VBA 代码来运行命令检查数据是否正确存档。”
与流行的观点相反,Access 确实支持transactions(Access SQL 不支持事务的说法是正确的,但我们仍然可以在 VBA 代码中使用事务)。因此,修改this post 中的代码以在工作区中使用事务,我相信这可以解决问题(在 Access 2010 中使用 DAO 进行了测试)。
锁定、获取计数和解锁的代码并不是真正需要的,并且可能会增加实施存档的难度,因为它要求在您更新表时没有人写入表。如果它确实发现了问题,Access 确实不支持事务日志记录,因此您将有一个非常简短的选项列表来解决它。但听起来你想绝对确定计数是正确的,所以这增加了另一个级别,可以说是不必要的检查。
Option Compare Database
Option Explicit
Sub ArchiveOldRecords()
Dim nSourceCount As Long, nMoveCount As Long, nDestCount As Long
Dim strSQL As String, sMsg As String
Dim rsLock As DAO.Recordset
Dim rsBefore As DAO.Recordset, rsAfter As DAO.Recordset
Dim wrk As Workspace, db As DAO.Database
Const strcTableSource As String = "t_TestWithDate" ' Move records FROM table
Const strcTableArch As String = "t_ArchiveTestWithDate" ' Move records TO table
Const strcWHERE As String = " WHERE field2 < " _
& "DATEADD(""yyyy"", -1, Date())" ' Select date field and DATEADD params
Const strcCount As String = "SELECT COUNT(*) As "
On Error GoTo TrapError
Set db = CurrentDb
Set wrk = DBEngine.Workspaces(0)
' Lock table - so no one can add/delete records until count is verified
Set rsLock = db.OpenRecordset(strcTableSource, dbOpenTable, dbDenyWrite)
' Get initial table counts
Set rsBefore = db.OpenRecordset( _
strcCount & "SourceCount, " _
& "(SELECT COUNT(*) FROM " & strcTableSource _
& strcWHERE & ") As MoveCount, " _
& "(SELECT COUNT(*) FROM " & strcTableArch & ") As DestCount " _
& "FROM " & strcTableSource & ";", dbOpenForwardOnly)
nSourceCount = rsBefore!SourceCount
nMoveCount = rsBefore!MoveCount
nDestCount = rsBefore!DestCount
rsBefore.Close
wrk.BeginTrans
' Copy records
strSQL = "INSERT INTO " & strcTableArch _
& " SELECT * FROM " & strcTableSource & " " & strcWHERE & ";"
db.Execute strSQL, dbFailOnError
' Unlock table - only needed for counts
rsLock.Close
Set rsLock = Nothing
' Delete copied records
strSQL = "DELETE * FROM " & strcTableSource & " " & strcWHERE & ";"
db.Execute strSQL, dbDenyWrite + dbFailOnError
' Lock table - only needed for counts
Set rsLock = db.OpenRecordset(strcTableSource, dbOpenTable, dbDenyWrite)
wrk.CommitTrans
' Get final table counts
Set rsAfter = db.OpenRecordset( _
strcCount & "SourceCount, " _
& "(SELECT COUNT(*) FROM " & strcTableSource _
& strcWHERE & ") As MoveCount, " _
& "(SELECT COUNT(*) FROM " & strcTableArch & ") As DestCount " _
& "FROM " & strcTableSource & ";", dbOpenForwardOnly)
' Double-check counts
If (rsAfter!SourceCount <> nSourceCount - nMoveCount) _
Or (rsAfter!DestCount <> nDestCount + nMoveCount) _
Or (rsAfter!MoveCount > 0) Then
sMsg = vbNewLine
sMsg = sMsg & "Records in " & strcTableSource & " before: "
sMsg = sMsg & nSourceCount
sMsg = sMsg & vbTab & "after: "
sMsg = sMsg & rsAfter!SourceCount
sMsg = sMsg & vbNewLine
sMsg = sMsg & "Records to archive from " & strcTableSource & ": "
sMsg = sMsg & nMoveCount
sMsg = sMsg & vbTab & "after: "
sMsg = sMsg & rsAfter!MoveCount
sMsg = sMsg & vbNewLine
sMsg = sMsg & "Records in " & strcTableArch & " before: "
sMsg = sMsg & nDestCount
sMsg = sMsg & vbTab & "after: "
sMsg = sMsg & rsAfter!DestCount
MsgBox "Count double-check failed" & sMsg
End If
Exit_Sub:
On Error Resume Next
' Unlock table and close recordsets
rsLock.Close
rsBefore.Close
rsAfter.Close
Set rsBefore = Nothing
Set rsAfter = Nothing
Set rsLock = Nothing
Set db = Nothing
Set wrk = Nothing
Exit Sub
TrapError:
MsgBox "Failed: " & Err.Description
wrk.Rollback
Err.Clear
Resume Exit_Sub
End Sub
【讨论】:
【参考方案2】:没有MOVE
命令,但您可以将记录复制到目标,然后在确定没有粘贴错误时使用类似的查询将它们从源中删除。
INSERT INTO MyArchive (fld1, fld2, fld3, fld4) SELECT fld1, fld2, fld3, fld4 FROM MyTable WHERE fld4 < DATEADD("y", -5, Date())
这会复制超过 5 年的所有内容。确认转账后,
DELETE * FROM MyTable WHERE fld4 < DATEADD("y", -5, Date())
这不是我的想法,我在 T-SQL 和 MS Access 之间进行了相当多的转换,但我认为这是非常可靠的 Access 查询代码。您自己的字段列表会有所不同。
【讨论】:
是的,这对我来说是正确的。 MSDN 文章 here 中的完整详细信息。 @GordThompson - +1 感谢您确认该代码。我还认为如果尚未创建目标存档表,SELECT INTO
可能更合适。 SELECT...INTO Statement
是的,你也是对的。 INSERT INTO (...) SELECT ...
是 Access 所称的“追加查询”,SELECT ... INTO ...
是 Access 所称的“生成表查询”。
不要忘记使用正确的错误处理,以便在成功完成 INSERT INTO 之前不会删除数据。此声明针对 OP,而不是 Jeeped。
@JohnnyBones 如何检查插入选择是否成功且没有错误?如何确保插入到 ... 选择命令的结果是所有或没有数据传输?以上是关于如何编写 SQL 命令将所有旧数据从访问表移动到另一个数据库/表?的主要内容,如果未能解决你的问题,请参考以下文章
如何将数据从一个表移动到另一个表并更新外键 (T-SQL 2008)