2014-01-28 82 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