Excel 使用vba更改odbc连接的commandText时出现问题

Excel 使用vba更改odbc连接的commandText时出现问题,excel,odbc,excel-2010,vba,Excel,Odbc,Excel 2010,Vba,我花了两天时间寻找解决方案,这让我发疯 首先让我解释一下我在做什么。我们带来了50万条记录,这些记录驱动了十几个数据透视表。为了保持工作簿的可管理文件大小,我直接从外部数据连接构建了pivot表。这是我手动配置的odbc连接 这一切都很好,我可以点击工作簿中的“全部刷新”,所有数据透视表都会自动更新,太棒了 但现在我需要能够用手动开始和结束日期限制整个记录集。。。更改数据透视表上的日期筛选器并不理想,因为它不仅仅是受结束日期影响的单个字段,还有一些字段需要在数据透视之前进行计算,这些字段的值取决

我花了两天时间寻找解决方案,这让我发疯

首先让我解释一下我在做什么。我们带来了50万条记录,这些记录驱动了十几个数据透视表。为了保持工作簿的可管理文件大小,我直接从外部数据连接构建了pivot表。这是我手动配置的odbc连接

这一切都很好,我可以点击工作簿中的“全部刷新”,所有数据透视表都会自动更新,太棒了

但现在我需要能够用手动开始和结束日期限制整个记录集。。。更改数据透视表上的日期筛选器并不理想,因为它不仅仅是受结束日期影响的单个字段,还有一些字段需要在数据透视之前进行计算,这些字段的值取决于涉及结束日期的公式

在一整个下午反复破坏Excel之后,我发现了一个限制,即如果您的连接直接连接到数据透视表,那么您不能使用?在“参数”对话框中指向单元格引用时,一旦关闭书本,单元格引用将丢失

所以我的下一个方法是这样做:

Dim ReportStartDate, ReportEndDate

' Get parameters from Intro sheet
ReportStartDate = "'" & ActiveWorkbook.Worksheets("Intro").Range("$B$1").Value & "'"
ReportEndDate = "'" & ActiveWorkbook.Worksheets("Intro").Range("$B$2").Value & "'"

' There are 3 directpivot odbc connections/caches that need to be modified.
' In each query, the default report-end-date is specified by CURDATE().
' The default report-start-date is specified as '2010-01-01'
' Replace these defaults with the values retrieved above.

Dim cn As WorkbookConnection
Dim odbcCn As ODBCConnection
Dim originalsqltext, newsqltext

For Each cn In ThisWorkbook.Connections     ' loop through the connections
    If cn.Type = xlConnectionTypeODBC Then
        Set odbcCn = cn.ODBCConnection
        originalsqltext = odbcCn.CommandText
        If odbcCn.Parent = "Calls" Then
            newsqltext = Replace(originalsqltext, "CURDATE()", ReportEndDate)
            newsqltext = Replace(newsqltext, "'2010-01-01'", ReportStartDate)
        ElseIf odbcCn.Parent = "Suboutcomes" Then
            newsqltext = Replace(originalsqltext, "CURDATE()", ReportEndDate)
            newsqltext = Replace(newsqltext, "'2010-01-01'", ReportStartDate)
        ElseIf odbcCn.Parent = "QtyCallsPerDay1" Then
            newsqltext = Replace(originalsqltext, "CURDATE()", ReportEndDate)
        Else
            newsqltext = originalsqltext
        End If
        odbcCn.CommandText = newsqltext
        odbcCn.Refresh
        odbcCn.CommandText = originalsqltext
    End If
    cn.Refresh ' refresh the other connection without modification
Next
Set cn = Nothing
Set odbcCn = Nothing
但是当它到达odbcCn.CommandText=newsqltext时,它会给我一个错误 运行时错误'1004: 应用程序定义或对象定义错误。 这太没用了

我验证了newsqltext包含了我想要的内容,但它不会分配回CommandText

在又一天的谷歌搜索和一些简单的宏记录实验之后,改变CommandText似乎需要像

.CommandText = Array( _
"SELECT C1.CALLID, C1.TAKENON, C1A.TAKENAT, CAST(CONCAT(DATE_FORMAT(TAKENON,'%c/%e/%y'),' ',TIME_FORMAT(TAKENAT,'%H:" _
    , _
    "%i:%s')) AS CHAR) AS CallDateTime, YEAR(C1.TAKENON) AS Year, CEILING(MONTH(C1.TAKENON)/3) AS Quarter, MONTH(C1.TAKE" _
    , _
(剩下的我就不说了,因为它太大了)。。。起初,我认为这是我的问题,因为当我最初尝试录制宏时,我遇到了一个“太多行连续”错误,所以我尽可能缩短查询,在替换之前将其减少到1428个字符。替换后,它最终达到1448个字符。。。但是如何将其解析为代码所需的数组格式呢?还是有更好的方法

我真的不想像这样破坏我的查询,只是为了能够用vba编辑它们,我觉得我只是缺少了一些关于如何修改CommandText的东西

在我的搜索中发现了一些令人不安的事情,比如这个问题:无法更改odbc连接上的CommandText,除非您先将其更改为oledb,然后您可以更改CommandText,然后将连接更改回odbc。。。但那是在Excel 2010之前,它不再使用这些

在那里链接到的KnowledgeBase文章更令人震惊。。。当我看到StringToArray函数时,我以为我找到了一个解决方案,但后来我进一步阅读并看到了

注意:如果使用共享数据透视缓存、基于OLAP的数据透视表或基于多个合并范围的数据透视表连接到数据库,则前面的代码可能无法正常工作

然后

如果工作表上的多个数据透视表是从同一数据透视表派生的,则子例程在处理第一个数据透视表后将不工作。截至2003年3月,没有已知的解决此问题的方法

尽管它注意到这篇文章只适用于Excel2000到2003

还有一件事我试过了,我想也许我可以用一下?参数,然后用vba设置它们。。。但是,当我创建了一个带有参数的简单查询,然后记录了一个宏,同时我将参数指向新的单元格引用时,该宏只包含以下内容: 子参数5() ' '参数5宏 "

我尝试使用direct-into-pivot类型的连接,以及连接到外部数据源的常规表(我知道它支持参数)

所以。。。有人知道为共享数据透视缓存odbc连接参数化查询的正确方法吗

更新: 我试过这个:

Dim cn, originalCn, newCn As WorkbookConnection
Dim odbcCn As ODBCConnection
Dim originalsqltext, newsqltext
Dim connStr As String

For Each cn In ThisWorkbook.Connections     ' loop through the connections
    If cn.Type = xlConnectionTypeODBC Then
        Set odbcCn = cn.ODBCConnection
        originalsqltext = odbcCn.CommandText
        Set originalCn = cn
        connStr = odbcCn.Connection
        Select Case odbcCn.Parent
            Case "Calls", "Suboutcomes"
                newsqltext = Replace(originalsqltext, "CURDATE()", ReportEndDate)
                newsqltext = Replace(newsqltext, "'2010-01-01'", ReportStartDate)
            Case "QtyCallsPerDay1"
                newsqltext = Replace(originalsqltext, "CURDATE()", ReportEndDate)
            Case Else
                newsqltext = originalsqltext
        End Select
        Set newCn = ActiveWorkbook.Connections.Add(odbcCn.Parent & "New", "WhoCares", connStr, newsqltext)
        Set cn = newCn
        cn.Refresh
        Set cn = originalCn
        newCn.Delete
    Else
        cn.Refresh ' refresh any other connections without modification
    End If
Next

Set cn = Nothing
Set odbcCn = Nothing
Set newCn = Nothing
Set originalCn = Nothing
虽然cn.Refresh似乎在让commandtext变成我想要的方面做了我想做的事情,但当我逐步完成时,它什么也做不了。如果它正在刷新,但我的数据透视没有更新,我可以看到它们可能在哪里查找调用1,在刷新发生时,它被命名为Calls1New,但连接没有做任何事情(查询通常需要几分钟才能完成)。或者我无法将其分配给同名的现有连接?在我设置cn=newCn之后,它们看起来完全相同,名称相同

我会再找一些,但如果有人做过类似的事情,我会感谢更多的帮助。非常感谢你迄今为止所付出的一切

编辑:所以我回到了原始状态

odbcCn.CommandText = newsqltext
cn.Refresh
odbcCn.CommandText = originalsqltext
我也试过了 odbcCn.CommandText=StringToArray(newsqltext) cn.刷新 odbcCn.CommandText=StringToArray(originalsqltext) 那是我在。两个人都没有工作

我将发布originalsqltext和newsql文本,因为它们就在错误之前。注意,如果我手动将originalsqltext粘贴到查询的对话框中,与newsqltext一样,originalsqltext也可以正常工作

**由于新信息,已删除以前的编辑**

注意-我发现了一个类似问题的线程-因为我已经测试过试图分配odbcCn.CommandText=originalsqltext(它没有以任何方式被更改),但它也失败了。然而,这个线程是从2009年开始的,所以它很可能不使用Excel2010,因为我试图编写

For Each pvtC In ThisWorkbook.PivotCaches
    name = pvtC.WorkbookConnection.name
    originalsqltext = pvtC.CommandText
    pvtC.CommandText = originalsqltext
Next
它在pvtC.CommandText=originalsqltext时也会失败

更新: 我现在确定这与查询本身无关,而是多个数据透视表指向同一数据透视缓存的条件。 我用简单的查询创建了一个新的外部数据源

SELECT * FROM clientdashboard1.Calls1 WHERE TAKENON BETWEEN '2010-01-01' AND CURDATE()
作为它的疑问。我将连接命名为AlphaTest,并从中创建了一个数据透视表,然后将该数据透视表复制到另一个表中,并使用不同的字段。
SELECT * FROM clientdashboard1.Calls1 WHERE TAKENON BETWEEN '2010-01-01' AND CURDATE()
For Each pvtC In ThisWorkbook.PivotCaches
    name = pvtC.WorkbookConnection.name
    If name = "AlphaTest" Then
        originalsqltext = pvtC.CommandText
        pvtC.CommandText = originalsqltext
    End If
Next
For Each cn In ThisWorkbook.Connections     ' loop through the connections
    If cn.Type = xlConnectionTypeODBC Then
        Set odbcCn = cn.ODBCConnection
        originalsqltext = odbcCn.CommandText
        Select Case odbcCn.Parent
            Case "Calls", "Suboutcomes"
                newsqltext = Replace(originalsqltext, "CURDATE()", ReportEndDate)
                newsqltext = Replace(newsqltext, "'2010-01-01'", ReportStartDate)
            Case "QtyCallsPerDay1"
                newsqltext = Replace(originalsqltext, "CURDATE()", ReportEndDate)
            Case Else
                newsqltext = originalsqltext
        End Select
        odbcCn.CommandText = newsqltext
        odbcCn.Refresh
        odbcCn.CommandText = originalsqltext
    Else ''' this used to be End If
        cn.Refresh ' refresh the other connection without modification
    End If
Next
somevar = "line 1" & _
          "line 2" & _
          .....
          "line 55"
somevar = somevar & _
          "line 56" & _
          "line 57"
ActiveWorkbook.Connections("ExampleConnection").Refresh
ActiveWorkbook.Connections("ExampleConnection").ODBCConnection.CommandText = _
"SELECT FILE1.FIELD1 AS ""Name1"", FILE1.FIELD2 as ""Name2""" & chr(13) & "" & chr(10) & chr(13) & "" & chr(10) & _
"FROM SERVER.LIBRARY.FILE1 FILE1" & chr(13) & "" & chr(10) & chr(13) & "" & chr(10) & _
"WHERE FILE1.FIELD1 = 'FILTER'"
ActiveWorkbook.Connections("ExampleConnection").Refresh
DIM str AS STRING

str = "VARIABLE"

ActiveWorkbook.Connections("ExampleConnection").ODBCConnection.CommandText = _
"SELECT FILE1.FIELD1 AS ""Name1"", FILE1.FIELD2 as ""Name2""" & chr(13) & "" & chr(10) & chr(13) & "" & chr(10) & _
"FROM SERVER.LIBRARY.FILE1 FILE1" & chr(13) & "" & chr(10) & chr(13) & "" & chr(10) & _
"WHERE FILE1.FIELD1 = '" & str & "'"
ActiveWorkbook.Connections("ExampleConnection").Refresh
ActiveWorkbook.Connections("ExampleConnection").Refresh
Sheet1.PivotTables("PivotTable1").PivotCache.Refresh
Sheet1.PivotTables("PivotTable2").PivotCache.Refresh
Sheet2.PivotTables("PivotTable1").PivotCache.Refresh
Sheet2.PivotTables("PivotTable2").PivotCache.Refresh
DIM s AS STRING
DIM f AS STRING
DIM w AS STRING
DIM r AS RANGE
Dim str AS STRING

set r = Sheet1.Range("A1")
str = r.Value
s = "SELECT FILE1.FIELD1 as ""Name1"", FILE1.FIELD2 as ""Name2"""
f = "FROM SERVER.LIBRARY.FILE1 FILE1"

If r.Value = "" Then
   w = ""
Else
   w = "WHERE FILE1.FIELD1 = '" & str & "'"
End If

ActiveWorkbook.Connections("ExampleConnection").ODBCConnection.CommandText = _
s & chr(13) & "" & chr(10) & chr(13) & "" & chr(10) & _
f & chr(13) & "" & chr(10) & chr(13) & "" & chr(10) & _
w
ActiveWorkbook.Connections("ExampleConnection").Refresh
Option Explicit

Sub UpdateWorkbookConnection(WorkbookConnectionObject As WorkbookConnection, Optional ByVal CommandText As String = "", Optional ByVal ConnectionString As String = "")

With WorkbookConnectionObject
    If .Type = xlConnectionTypeODBC Then
        If CommandText = "" Then CommandText = .ODBCConnection.CommandText
        If ConnectionString = "" Then ConnectionString = .ODBCConnection.Connection
        .ODBCConnection.Connection = Replace(.ODBCConnection.Connection, "ODBC;", "OLEDB;", 1, 1, vbTextCompare)
    ElseIf .Type = xlConnectionTypeOLEDB Then
        If CommandText = "" Then CommandText = .OLEDBConnection.CommandText
        If ConnectionString = "" Then ConnectionString = .OLEDBConnection.Connection
    Else
        MsgBox "Invalid connection object sent to UpdateWorkbookConnection function!", vbCritical, "Update Error"
        Exit Sub
    End If
    If StrComp(.OLEDBConnection.CommandText, CommandText, vbTextCompare) <> 0 Then
        .OLEDBConnection.CommandText = CommandText
    End If
    If StrComp(.OLEDBConnection.Connection, ConnectionString, vbTextCompare) <> 0 Then
        .OLEDBConnection.Connection = ConnectionString
    End If
    .Refresh
End With

End Sub
UpdateWorkbookConnection ActiveWorkbook.Connections("Connection"), "exec sp_MyAwesomeProcedure"
WorkbookConnection wc = book.Connections.Add("SQL-STRING", "", response.ConnectionString, response.SqlString, XlCmdType.xlCmdSql);
pivot = worksheet.PivotTables(1);
pivot.ChangeConnection(wc);