【问题标题】:Executing queries using DAO使用 DAO 执行查询
【发布时间】:2009-09-09 14:36:27
【问题描述】:

我想使用 DAO 对 Access 数据库执行查询列表。 “Database.Execute()”方法似乎适合此,它只能执行“操作查询”,即不返回结果集的查询 (MSDN reference)。对于返回记录的查询,可以使用“Database.OpenRecordset()”。如果传递了错误类型的查询,这两种方法都会抛出异常。

在列表中有操作查询和选择查询,我如何预先决定哪些会返回记录,哪些不会?

【问题讨论】:

  • 为什么您甚至想要执行仅是 SELECT 的查询,除非您要为用户显示它们?除非您使用表单或报表,否则您不应该在 Access 中执行此操作。
  • 您确实需要解释您要完成的工作,因为执行任意连续的 SELECT 和 DML 查询在现实世界中没有任何意义。
  • 当您想要检查数据访问层时,这对于(单元)测试目的是有意义的:检查之前/之后的状态,并可能恢复到某个状态。
  • @David 列表中的每个查询首先保存在数据库中,然后执行。这样做的原因是某些查询以后可以使用以前保存的查询。当然不需要执行 SELECT 查询,这正是我想知道哪些查询返回结果的原因。

标签: delphi ms-access dao


【解决方案1】:

请注意,无论查询是否返回结果集,都可以使用 ADO Execute 方法。

但是您真的要执行所有“查询”吗?如果它们包含 SQL DDL 怎么办?假设您使用此 SQL DDL 代码创建了 PROCEDURE

CREATE PROCEDURE qryDropMe AS DROP PROCEDURE qryDropMe;

;)

【讨论】:

    【解决方案2】:

    Access 维护一个名为 MSysObjects 的隐藏系统表,其中包含一个字段(标志),该字段存储一个指示查询类型的值。您可以使用列表中的每个查询名称尝试以下函数,并使用返回值来确定是使用 Database.Execute() 还是 Database.OpenRecordset()

    该函数需要 MSysObjects 的读取权限。我听说过一些 Access 2007 用户被拒绝读取 MSysObjects 的报告。但是,我在 Access 2007 中没有遇到过这个问题。

    我测试了几种查询类型以确定标志值。如果您的其中一个查询是我未测试的类型,则该函数将返回 Flags 值无法识别。您可以修改函数以包含该标志类型。

    我测试的唯一 DDL 查询是 DROP TABLE (Flags = 96)。

    另外,请注意,并非所有“SELECT ... FROM ...”查询都是用于您的目的的选择查询(返回记录集)。诸如“SELECT fields INTO newtable FROM oldtable;”之类的查询不返回记录,Access UI 将其归类为 Make Table 查询。

    Public Function QueryType(ByVal pQueryName As String) As String
        Dim lngFlags As Long
        Dim strType As String
        Dim strCriteria As String
    
        strCriteria = "[Name] = """ & pQueryName & """ And [Type] = 5"
        lngFlags = DLookup("Flags", "MSysObjects", strCriteria)
    
        Select Case lngFlags
        Case 0
            strType = "Select"
        Case 16
            strType = "Crosstab"
        Case 32
            strType = "Delete"
        Case 48
            strType = "Update"
        Case 64
            strType = "Append"
        Case 80
            strType = "Make Table"
        Case 96
            strType = "Drop Table"
        Case 128
            strType = "Union"
        Case Else
            strType = "Flags " & CStr(lngFlags) & " unrecognized"
        End Select
    
        QueryType = strType
    End Function
    

    【讨论】:

    • 逻辑上,UPDATE 是 DELETE 和 INSERT。以上表明对于 Access 数据库引擎,UPDATE 是 DELETE 和交叉表!
    • 有一天。不更新记录中所有字段的 UPDATE 如何成为逻辑 DELETE 和 INSERT?
    • @oneday - 我认为你正在阅读更多没有意义的地方 - 标志列表就是 - 标志列表
    • @DJ:根据我的经验,一个名为 'flags' 的枚举暗示了一个位掩码,更不用说值 (16, 32, 64, ....)。
    • @Tony Toews:因为它使用未提及的列的现有值并且一次发生。你曾经使用过 SQL Server ON UPDATE 触发器吗?您将拥有两个名为“inserted”和“deleted”的逻辑表,而不是一个名为“updated”的逻辑表。
    【解决方案3】:

    受@HansUp 回答的启发,我对 DAO 接口提供的 QueryDef 结构进行了更多调查。该结构有一个“类型”属性,我可以使用它来区分不同的查询类型 (MSDN)。我最终得到了以下实现:

    function TAccessDatabase.SQLExec(AName, AQuery: String): Integer;
    var
      I: Integer;
      QDef: QueryDef;
      QDefExists: Boolean;
    begin
      Result := 0;
    
      // Lookup querydef in the database
      QDefExists := False;
      for I := 0 to DB.QueryDefs.Count - 1 do
      begin
        QDef := DB.QueryDefs[I];
        if QDef.Name = AName then
        begin
          QDefExists := True;
          break; //-->
        end;
      end;
    
      // Create query def if it doesn't exists
      if not QDefExists then
      begin
        QDef := DB.CreateQueryDef(AName, AQuery);
        // Refresh is required to get the correct QDef.Type_
        DB.QueryDefs.Refresh;
      end;
    
      // Execute the query only if it is not a SELECT
      if QDef.Type_ <> dbQSelect then
      begin
        db.Execute(AQuery, dbInconsistent);
        Result := DB.RecordsAffected;
      end;
    end;
    

    感谢大家的有用回答和评论。

    【讨论】:

    • 抱歉,我完全忽略了 Delphi 标签。与 MSysObjects Flags 相比,我更喜欢您的 QueryDef 类型方法。
    • @HansUp 既然您说接受我自己的答案不会感到那么奇怪。再次感谢您的提示。
    【解决方案4】:

    你为什么不捕捉抛出的异常并分析它?

    您有什么方法可以使用 beginTrans/Rollback 指令吗?然后你可以发送你的 SQL 命令,收集错误,然后回滚你的事务,让你的数据库保持不变。

    如何使用 ADO 连接,它比 ADO 连接更智能,其中连接保存“错误”集合并返回一些其他数据,如受影响的记录数?

    【讨论】:

      【解决方案5】:

      此信息适用于查询的类型。所以:

      • 所有执行SELECT ... FROM ...的查询都是选择查询
      • 所有INSERTUPDATEDELETE都是动作查询

      您只需检查查询 sql 命令文本以查看它是否以上述任何关键字开头并采取相应措施。

      【讨论】:

      • “执行 SELECT ... FROM ... 的所有查询都是选择查询”——但它们不会返回结果集,例如考虑“查询”是否包含 SQL DDL“CREATE VIEW MyView AS SELECT CustomerID FROM Customers;”
      • 这不是一个 SELECT 查询,它是一个 CREATE 查询并且不会给出任何结果......这将属于“动作查询”类别,对于所有 DDL 查询都是如此。
      • 那么回到最初的问题:“我如何预先决定哪些会返回记录,哪些不会?”换句话说,如何判断包含 SELECT 关键字的查询是否会返回结果集? INSERT INTO..SELECT.. 是另一个示例构造。
      • 这个也不会返回任何结果集,因为它基本上是一个插入。引用我的回答:“...检查查询 sql 命令文本以查看它是否以上述任何关键字开头...”随意根据需要向 2 个组添加一些其他关键字(例如 DDL 语句 CREATE、DROP、 ...)
      • 嗯,以 PARAMETERS 关键字开头,后跟 SELECT 关键字的查询呢?一个以 SELECT 关键字开始但随后使用 INTO [NewTableName] 并因此不返回结果集的方法怎么样?感谢您的提议,但我觉得我无法编辑您的答案来改进它;相反,它从根本上是有缺陷的,无论如何解析 SQL 并非易事。我不认为你在这里有一个可行的系统。
      猜你喜欢
      • 2012-05-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-05
      • 2019-04-04
      • 2010-12-20
      • 2011-03-08
      • 1970-01-01
      相关资源
      最近更新 更多