2014-01-28 40 views
1

我正在尝试使用MS Access对无法控制的ODBC服务器进行直通查询更新。我必须使用Passthrough的原因是我正在访问的记录有超过255个字段(如果可以的话,我会使用链接表)。MS Access Passthrough查询更新

我一直在使用这种资源使用直通获取数据(http://www.techonthenet.com/access/tutorials/passthrough/basics09.php

查询很简单:SELECT FullName, PointNumber FROM DNP3.CDNP3AnalogIn

ODBC连接字符串是:ODBC;DSN=SCX6_DB;LOCATION=Main;UID=admin;PWD=password;LOCALTIME=False;

现在Access数据库内我有一个表(SCADA数据库标记)具有相同名称的字段(FullName,PointNumber),并且我想使用Update Paschhrough查询更新ODBC数据库内的字段,但我不确定如何执行此操作。

我保存以前的查询作为DNP3_CDNP3AnalogIn查询,并设法使一个新的查询:

UPDATE [DNP3_CDNP3AnalogIn Query] INNER JOIN [SCADA DB Tags] ON 
[DNP3_CDNP3AnalogInQuery].FullName = [SCADA DB Tags].FullName 
SET [DNP3_CDNP3AnalogIn Query].[PointNumber] = [SCADA DB Tags].[PointNumber]; 

但我从Access得到一个错误:Operation must use an updateable query.

我知道有好歹做到这一点,但我似乎无法找到一个例子(我可能不会使用正确的词组)。微软网页(http://technet.microsoft.com/en-us/library/bb188204%28v=sql.90%29.aspx)说:There is, however, one important limitation: the results returned by SQL pass-through queries are always read-only. If you want to enable users to perform updates based on the data retrieved, you must write code to handle this.不幸的是它并没有给出一个例子来做到这一点!

任何人都可以给我一个解决方案,如果需要我可以使用VBA吗?如果需要,我也可以提供更多背景。不幸的是,我不是Access的专家,我只是试图想出一个可以节省我一些时间的自动化解决方案。

回答

2

当他们说:“如果你想使用户能够执行基于检索[从传递查询]数据更新,您必须编写代码来处理这个”他们大概意思是这样的:

Option Compare Database 
Option Explicit 

Public Sub UpdateSqlServer() 
    Dim cdb As DAO.Database, rst As DAO.Recordset 
    Dim con As Object ' ADODB.Connection 
    Dim cmd As Object ' ADODB.Command 
    Const adParamInput = 1 
    Const adInteger = 3 
    Const adVarWChar = 202 

    Set cdb = CurrentDb 
    Set rst = cdb.OpenRecordset(_ 
      "SELECT " & _ 
       "[SCADA DB Tags].FullName, " & _ 
       "[SCADA DB Tags].PointNumber " & _ 
      "FROM " & _ 
       "[DNP3_CDNP3AnalogIn Query] " & _ 
       "INNER JOIN " & _ 
       "[SCADA DB Tags] " & _ 
        "ON [DNP3_CDNP3AnalogIn Query].FullName = [SCADA DB Tags].FullName", _ 
      dbOpenSnapshot) 

    Set con = CreateObject("ADODB.Connection") 
    con.Open "DSN=SCX6_DB;" 
    Set cmd = CreateObject("ADODB.Command") 
    cmd.ActiveConnection = con 
    cmd.CommandText = _ 
      "UPDATE DNP3.CDNP3AnalogIn SET " & _ 
       "PointNumber=? " & _ 
      "WHERE FullName=?" 
    cmd.Parameters.Append cmd.CreateParameter("?", adInteger, adParamInput) ' PointNumber 
    cmd.Parameters.Append cmd.CreateParameter("?", adVarWChar, adParamInput, 255) ' FullName 
    cmd.Prepared = True 

    Do Until rst.EOF 
     cmd.Parameters(0).Value = rst!PointNumber 
     cmd.Parameters(1).Value = rst!FullName 
     cmd.Execute 
     rst.MoveNext 
    Loop 
    Set cmd = Nothing 
    con.Close 
    Set con = Nothing 
    rst.Close 
    Set rst = Nothing 
    Set cdb = Nothing 
End Sub 

注:

  1. 代码使用现有的ODBC DNS。
  2. 它使用Prepared Statement来执行更新,提高效率并防止与SQL注入相关的故障。
  3. 源记录集在传递查询上执行INNER JOIN,以确保代码只尝试更新服务器上服务器上实际上存在的行。
+0

Thankyou的详细响应。我在cmd.Execute(Microsoft ODBC驱动程序管理器:SQL数据类型超出范围)时收到错误消息。我想这是从PointNumber发送错误的类型。从数据库模式:PointNumber是一个Word(无符号)。我真的不确定如何使用这个演员而不是Integer,因为你已经放弃了? – reubenb87

+0

实际上问题出在FullName字段,因为它是一个字符串,它有'。'和''字符。如果我使用FullName示例对更新查询进行硬编码,它将起作用,例如“WHERE FullName ='这是一个测试全名'”。但是,如果我封装?喜欢 '?' (“WHERE FullName ='?'”)它不起作用。任何提示? – reubenb87

+0

解决了FullName字段的问题,使用adVarChar而不是adVarWChar,它的工作原理是!!!谢谢一堆!我只希望返回的错误容易调试。 – reubenb87

1

你是说[DNP3_CDNP3AnalogIn Query]是基于服务器端的,并且该表[SCADA DB标签]是基于本地的?在这种情况下,您不能使用传递查询。

但是,由于表格在不同的位置,所以传递虽然不能同时触摸两个。

然而,您可以在循环中执行“单个”服务器端(传递)。如果您设置传递查询并保存它,则此代码将起作用:

Dim qdfPass  As DAO.QueryDef 

    Dim rstLocal  As DAO.Recordset 
    Dim strSQL  As String 
    Dim strSQL2  As String 

    Set qdfPass = CurrentDb.QueryDefs("MyPass") 

    strSQL = "UPDATE [DNP3_CDNP3AnalogIn Query] " & _ 
      "SET [DNP3_CDNP3AnalogIn Query].[PointNumber] = 'xxxx' " & _ 
      "WHERE [DNP3_CDNP3AnalogInQuery].FullName = 'zzzz' " 

    Set rstLocal = CurrentDb.OpenRecordset("[SCADA DB Tags]") 

    Do While rstLocal.EOF = False 
    strSQL2 = Replace(strSQL, "xxxx", rstLocal!PointNumber) 
    strSQL2 = Replace(strSQL2, "zzzz", rstLocal!FullName) 
    qdfPass.SQL = strSQL2 
    qdfPass.Execute 
    rstLocal.MoveNext 
    Loop 

    rstLocal.Close