MS Access:使用 VBA 进行 SQL 插入的日期格式

Posted

技术标签:

【中文标题】MS Access:使用 VBA 进行 SQL 插入的日期格式【英文标题】:MS Access: Date format for SQL Insert using VBA 【发布时间】:2016-02-18 00:08:46 【问题描述】:

这可能是一个非常简单的问题,但到目前为止,我很难理解我在网上找到的信息。当我尝试从 VBA 中运行 SQl 时,我的 strSQl 出现类型不匹配。

我有一个包含 4 列的表 [tbl_DatabaseUpdateLog]

UpdateID(自动编号)

开始(日期/时间)

结束(日期/时间)

持续时间(日期/时间)

我必须定期从公司数据库中提取信息以进行较小的分析项目,并且我想记录运行这些查询的频率和时间。每次启动更新时,都会运行以下内容:

Dim StartUpdate, EndUpdate, LengthUpdate, strSQl As Date
StartUpdate = Time()
    
Call UpdateAll 'This calls the collection of queries and is irrelevant for my problem

EndUpdate = Time()
LengthUpdate = EndUpdate - StartUpdate
Forms!frm_Timer.Caption = "Update Completed at " & EndUpdate & " (" & Format(LengthUpdate, "HH:MM:SS") & ")"

    DoCmd.SetWarnings (0)
    strSQl = "INSERT INTO tbl_DatabaseUpdateLog ( Start, [End], Duration ) " & _
            "SELECT '" & StartUpdate & "' AS Started, '" & EndUpdate & "' AS Ended, '" & LengthUpdate & "' AS Lasted"
    DoCmd.RunSQL strSQl
    DoCmd.SetWarnings (-1)
    
DoCmd.Close acForm, Me.Name

我尝试在日期前后使用#'s 并使用 Now() 而不是 Time(),但我觉得我缺少一个基本概念来帮助我解决问题。如果有帮助,我只需要时间和持续时间(而不是日期本身)。

【问题讨论】:

我建议你应该尝试使用参数进行插入。这更安全,并且忽略了格式。因此,您可以更轻松地插入日期和时间。否则,您必须处理令人头疼的格式... MS Access 不是 mysql 或 SQL-Server。请不要使用不恰当的标签。 我很抱歉。我在学习这些东西时有点陷入困境,而且我对差异有点模糊,因为我只熟悉基本知识。感谢您的修复。 【参考方案1】:

当我尝试从我的 VBA 中运行 SQl 时,它给了我一个类型不匹配 在我的 strSQl 上

检查这两个语句...

Dim StartUpdate, EndUpdate, LengthUpdate, strSQl As Date
strSQl = "INSERT INTO tbl_DatabaseUpdateLog ( Start, [End], Duration ) " & _
    "SELECT '" & StartUpdate & "' AS Started, '" & EndUpdate & "' AS Ended, '" & LengthUpdate & "' AS Lasted"

当您声明strSQl As Date 时,这意味着strSQl 只能保存日期/时间值。 "INSERT INTO ...whatever" 等字符串不是日期/时间值。因此,当您尝试将此类字符串存储到 strSQl 时,Access 会抱怨数据类型不兼容:“类型不匹配”

由于您打算 strSQl 保存 SQL 语句的文本,因此以这种方式声明它(而不是 As Date):Dim strSQl As String

该更改应该可以帮助您克服错误。为了您的更大目标,请使用带有 DAO 的参数查询。

Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim StartUpdate As Date, EndUpdate As Date, LengthUpdate As Date
Dim strSQl As String

StartUpdate = Time()
Call UpdateAll 'This calls the collection of queries and is irrelevant for my problem
EndUpdate = Time()

LengthUpdate = EndUpdate - StartUpdate
Forms!frm_Timer.Caption = "Update Completed at " & EndUpdate & " (" & Format(LengthUpdate, "HH:MM:SS") & ")"

strSQl = "INSERT INTO tbl_DatabaseUpdateLog ([Start], [End], Duration) " & _
    "VALUES ([p1], [p2], [p3]);"
Set db = CurrentDb
Set qdf = db.CreateQueryDef(vbNullString, strSQl)
With qdf
    .Parameters("p1").Value = StartUpdate
    .Parameters("p2").Value = EndUpdate
    .Parameters("p3").Value = LengthUpdate
End With
qdf.Execute dbFailOnError
DoCmd.Close acForm, Me.Name

请注意,使用INSERT 的这种方法,日期格式 甚至都不是问题。由于StartUpdateEndUpdateLengthUpdate 都是有效的日期/时间值,您可以简单地将它们提供给参数,而无需担心格式和# 分隔符。

【讨论】:

我不敢相信我错过了将 strSQl 作为日期的小错误。那个小修复导致代码做我想要的,但我也尝试了你的代码,这样我将来可以学习一些更好的编码习惯。但是当我尝试它时,它给了我运行时错误 3061,“参数太少。预期为 3。” 它不要求任何东西,但它向我显示了带有 [Start]、[End] 和 [Duration] 参数的简化表我还注意到您的代码列出了 .Parameters("p2 ") 两次。我假设第二个是“p3” 我更正了第三行 .Parameters 并将 VALUES 列表中的参数名称括起来。这些变化对你有用吗? 是的,现在可以了!我已经把“p2”改成了“p3”,所以肯定是VALUES中的参数需要更新了。请问为什么这很重要? 我实际上并不相信括号很重要 --- 在我注意到我最初有两次“p2”之前,我添加了它们(就像出于绝望一样)。但是括号应该不会受伤,所以我离开了它们。【参考方案2】:

我看到您正在使用 INSERT INTO SELECT FROM (literal),而直接的 INSERT INTO (columns) VALUES (values) 会更简单。您还可以在单​​个 INSERT 语句中包含多个 VALUES 子语句,这很简洁。

我还看到您正在存储非规范化数据,很明显 Duration 可以从 End - Start 派生,因此您可能希望删除该列。

另外,请避免使用匈牙利表示法,在这种情况下,请在表名称前加上 tbl_ 前缀。现在已经不是 1980 年代了。不要这样做。

使用字符串连接的简短而简单的回答:

永远不要这样做

SQL 服务器

如果您使用的是 SQL Server,则需要将日期括在单引号中,并且需要转换为明确的日期格式字符串,例如 yyyy-MM-dd HH:mm:ss

strSQL = "INSERT INTO neverDoThis VALUES ( '" & Format( startUpdate, "yyyy-MM-dd HH:mm:ss" & "' )"

访问/喷射

如果您使用 Access 或 Jet,则需要将日期括在井号“#”中,并使用“MM/dd/yyyy hh:mm:ss tt”格式(愚蠢的美国人......)。

strSQL = "INSERT INTO neverDoThis VALUES ( #" & Format( startUpdate, "MM/dd/yyyy hh:mm:ss tt" & "# )"

这是Format 函数的参考:https://msdn.microsoft.com/en-us/library/office/gg251755.aspx?f=255&MSPPError=-2147217396

更好的答案:使用参数

改为这样做

SQL 服务器

SQL Server 使用命名参数,例如 @foo:

cmd.CommandText = "INSERT INTO databaseUpdateLog ( [Start], [End], [Duration] ) VALUES ( @start, @end, @duration )"
cmd.Parameters.Append cmd.CreateParameter( "@start", , , startUpdate )
cmd.Parameters.Append cmd.CreateParameter( "@end", , , endUpdate )
cmd.Parameters.Append cmd.CreateParameter( "@duration", , , lengthUpdate )

访问/喷射

Access 使用匿名占位符,按添加顺序访问,使用 `?':

cmd.CommandText = "INSERT INTO databaseUpdateLog ( [Start], [End], [Duration] ) VALUES ( ?, ?, ? )"
cmd.Parameters.Append cmd.CreateParameter( "?", , , startUpdate )
cmd.Parameters.Append cmd.CreateParameter( "?", , , endUpdate )
cmd.Parameters.Append cmd.CreateParameter( "?", , , lengthUpdate )

VBA 中的Append 方法是Sub,因此调用它时不使用括号。CreateParameter 方法在中间有 3 个可选参数:TypeDirectionSize,可以从值中推断出来,因此可以通过输入 , , , 来省略它们。

【讨论】:

如果提供 VBA 示例而不是 VB.NET 示例,您的“更好的答案:使用参数”部分会更好。 @GordThompson 我已修改我的答案以在 VBA 语法中使用 ADO。 如果您修改它以保持正确的 SQL 语法,它甚至会很有用。以下是 Format 函数的参考:Format Function (Visual Basic for Applications) Access/Jet 的 'hh:mm:ss tt' 中的 'tt' 是什么? 'tt' 没有记录在 VBA 文档 AFAICS 中,它不适用于 Access 2016 中的 'tt'。它在没有 'tt' 的情况下也能工作。【参考方案3】:

更直接的方法是使用原生 DAO:

Dim rs As DAO.Recordset
Dim StartUpdate As Date
Dim EndUpdate As Date
Dim LengthUpdate As Date

StartUpdate = Time    
Call UpdateAll 'This calls the collection of queries and is irrelevant for my problem.    
EndUpdate = Time
LengthUpdate = EndUpdate - StartUpdate
Forms!frm_Timer.Caption = "Update Completed at " & EndUpdate & " (" & Format(LengthUpdate, "h:mm:ss") & ")"

Set rs = CurrentDb.OpenRecordset("Select Top 1 * From tbl_DatabaseUpdateLog")
rs.AddNew
    rs!Start.Value = StartUpdate
    rs!End.Value = EndUpdate
    rs!Duration.Value = LengthUpdate
rs.Update
rs.Close    
Set rs = Nothing

DoCmd.Close acForm, Me.Name

【讨论】:

【参考方案4】:

只计算时间,在 VBA 中你可以使用这个表达式:

CDate(Now - Date())

将结果括在 # 中,因为结果仍然是 Date 类型,即使只存储时间信息。

您可以使用 DateDiff 函数来计算持续时间,您需要选择合适的单位。例如:

DateDiff(DateInterval.Second, EndUpdate, StartUpdate)

结果是一个长整数,因此您不需要将结果包含在 SQL 中。

建议您对 SQL 进行参数化,但不是必需的。

【讨论】:

为什么建议使用CDate(Now - Date()) 而不是Time()?要么给你一个日期/时间值,以 Dec 30 1899 作为日期部分:Debug.Print Format(CDate(Now - Date()), "mmm d yyyy hh:nn:ss"), Format(Time, "mmm d yyyy hh:nn:ss") TBH 这个问题不是很清楚,但我试图通过展示一种通用方法来回答“我只需要时间和持续时间(而不是日期本身)”部分从日期表达式中删除日期信息。正如您指出的那样,两者都给出了相同的答案。我希望 OP 在这里了解如何操作日期是 VB

以上是关于MS Access:使用 VBA 进行 SQL 插入的日期格式的主要内容,如果未能解决你的问题,请参考以下文章

编辑器中的 ms-access VBA 长 sql 查询字符串行拆分(内联双引号)

使用 VBA (MS Access) 中的 bigint 字段更新 SQL 表

使用 Excel VBA 查询 MS Access,SQL BETWEEN 日期查询

使用 VBA 或 PowerShell 将所有 MS Access SQL 查询导出到文本文件

如何使用查询或 VBA 和 SQL 更新 MS ACCESS 中的表

MS ACCESS, VBA 将外部 MS Access 表导入 SQL server 表